CEFSharp是我们做.NET桌面程序嵌入网站的一大利器,但是它也存在着各种各样的奇葩问题,尤其是遇到机器配置比较低的时候。
背景
就好比我们项目上的一个客户机上,只安装了4G内存,而且客户开着程序一直不关闭(可能持续了很多天),这样就导致了内存只增不减(当然也有可能是我们前端网站的问题,单页vue程序,用了比较多的全局缓存变量),结果就是导致CEFSharp的其中一个子进程崩掉了,界面变成了白屏。。。
这客户一看当然很不爽了,只会认为是我们程序的问题。
原因分析
经过分析,CEFSharp承载网页时,它会开启6个名为CefSharp.BrowserSubprocess.exe的子进程,如下:
- browser
- gpu-process
- utility
- utility
- renderer
- renderer
当内存达到上限时(默认大概是1GB左右,64位机器上试验的),gpu-process这个进程就会崩掉,从而导致界面白屏。异常日志信息如下:
ERROR:v8_initializer.cc(688)] V8 javascript OOM: (Reached heap limit).
INFO:crash_reporting.cc(211)] Crash reporting enabled for process: gpu-process
ERROR:gpu_init.cc(481)] Passthrough is not supported, GL is disabled, ANGLE is
当然你也可以通过杀进程的方式来重现白屏,不过最好还是通过代码的方式来模拟内存溢出比较接近于实际的情况。比如来个for循环拷贝window对象,部分代码如下
<template>
<div style="display: flex">
<input v-model:value="times"></input>
<button @click="loop()">循环{{ loopData.length }}</button>
</div>
</template>
export default {
name: 'HeapDemo',
data() {
return {
loopData: [],
times: 100000,
},
methods: {
loop() {
for (let index = 0; index < this.times; index++) {
this.loopData.push(Object.assign({}, window));
}
}
}
解决办法
既然是由于内存上限导致的,那么就来给CEFSharp增加内存使用上限,代码如下:
var setting = new CefSettings();
//其他代码忽略......
//设置浏览器可使用的最大内存值为2GB(2048)
setting.CefCommandLineArgs.Add("--js-flags", $"--max_old_space_size=2048");
if (!Cef.IsInitialized)
{
Cef.Initialize(setting);
}
这样可以最大限度的减少崩溃的几率,还有一种方式就是监控CefSharp.BrowserSubprocess.exe进程,当发现进程数少于6个时,就重新加载CEF浏览器,这样可以避免gpu-process崩溃时,程序界面一直处于白屏状态。
上述都是一种折中方案,虽然不是治本的方式,但是比较有效的快速解决方案。
要想彻底解决,最终还是要从前端源码来分析,减少不必要的内存开销!