- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我正在开发一个提供简单 HTTP/HTTPS 服务器的 Android 应用程序。如果配置了 HTTPS 服务,那么在每个连接上都会观察到 native 内存使用量增加,最终导致应用程序崩溃 (oom),而使用 HTTP 配置可保持 native 内存使用量相对恒定。应用程序的 Java VM 在两种配置中保持相对恒定。
该应用程序提供一个 HTML 页面,其中包含一个带有定期轮询的 javascript(每秒一次 json 轮询),因此使用 HTTPS 配置调用应用程序页面并保持页面打开几个小时将导致提到的超出-内存,因为 native 内存使用量增加。我已经测试了许多在互联网上找到的 SSLServerSocket 和 SSLContext 配置,但都没有运气。
我在各种 Android 设备和从 2.2 到 4.3 的各种 Android 版本上观察到同样的问题。
处理客户端请求的代码对于两种配置 HTTP/HTTPS 是相同的。两种配置之间的唯一区别是服务器套接字的设置。而在 HTTP 服务器套接字的情况下,单行类似于“ServerSocket serversocket = new ServerSocket(myport);”完成这项工作,在 HTTPS 服务器设置的情况下,通常会采取设置 SSLContext 的步骤——即设置 key 管理器并初始化 SSLContext。目前,我使用默认的 TrustManager。
有人知道使用 OpenSSL 的 Android 的默认 TLS 提供程序中存在任何内存泄漏问题吗?为了避免 native 内存泄漏,我应该考虑一些特别的事情吗?任何提示都非常感谢。
更新:我还尝试了 TLS 提供者:OpenSSL 和 JSSE,方法是在 SSLContext.getInstance("TLS", providerName) 中明确给出提供者名称。但这并没有改变什么。
这是一个演示问题的代码块。只需创建一个示例应用程序,将其放入主 Activity 的 onCreate 底部并构建并运行该应用程序。确保您的 Wifi 已打开并通过以下地址调用 HTML 页面:
https://android device IP:9090
然后看adb日志,过一会你会看到native memory开始增加了。
new Thread(new Runnable() {<p></p>
<p>public void run() {</p>
final int PORT = 9090;
SSLContext sslContext = SSLContext.getInstance( "TLS" ); // JSSE and OpenSSL providers behave the same way
KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
KeyStore ks = KeyStore.getInstance( KeyStore.getDefaultType() );
char[] password = KEYSTORE_PW.toCharArray();
// we assume the keystore is in the app assets
InputStream sslKeyStore = getApplicationContext().getResources().openRawResource( R.raw.keystore );
ks.load( sslKeyStore, null );
sslKeyStore.close();
kmf.init( ks, password );
sslContext.init( kmf.getKeyManagers(), null, new SecureRandom() );
ServerSocketFactory ssf = sslContext.getServerSocketFactory();
sslContext.getServerSessionContext().setSessionTimeout(5);
try {
SSLServerSocket serversocket = ( SSLServerSocket )ssf.createServerSocket(PORT);
// alternatively, the plain server socket can be created here
//ServerSocket serversocket = new ServerSocket(9090);
serversocket.setReceiveBufferSize( 8192 );
int num = 0;
long lastnatmem = 0, natmemtotalincrease = 0;
while (true) {
try {
Socket soc = (Socket) serversocket.accept();
Log.i(TAG, "client connected (" + num++ + ")");
soc.setSoTimeout(2000);
try {
SSLSession session = ((SSLSocket)soc).getSession();
boolean valid = session.isValid();
Log.d(TAG, "session valid: " + valid);
OutputStream os = null;
InputStream is = null;
try {
os = soc.getOutputStream();
// just read the complete request from client
is = soc.getInputStream();
int c = 0;
String itext = "";
while ( (c = is.read() ) > 0 ) {
itext += (char)c;
if (itext.contains("\r\n\r\n")) // end of request detection
break;
}
//Log.e(TAG, " req: " + itext);
} catch (SocketTimeoutException e) {
// this can occasionally happen (handshake timeout)
Log.d(TAG, "socket timeout: " + e.getMessage());
if (os != null)
os.close();
if (is != null)
is.close();
soc.close();
continue;
}
long natmem = Debug.getNativeHeapSize();
long diff = 0;
if (lastnatmem != 0) {
diff = natmem - lastnatmem;
natmemtotalincrease += diff;
}
lastnatmem = natmem;
Log.i(TAG, " answer the request, native memory in use: " + natmem / 1024 + ", diff: " + diff / 1024 + ", total increase: " + natmemtotalincrease / 1024);
String html = "<!DOCTYPE html><html><head>";
html += "<script type='text/javascript'>";
html += "function poll() { request(); window.setTimeout(poll, 1000);}\n";
html += "function request() { var xmlHttp = new XMLHttpRequest(); xmlHttp.open( \"GET\", \"/\", false ); xmlHttp.send( null ); return xmlHttp.responseText; }";
html += "</script>";
html += "</head><body onload=\"poll()\"><p>Refresh the site to see the inreasing native memory when using HTTPS: " + natmem + " </p></body></html> ";
byte[] buffer = html.getBytes("UTF-8");
PrintWriter pw = new PrintWriter( os );
pw.print("HTTP/1.0 200 OK \r\n");
pw.print("Content-Type: text/html\r\n");
pw.print("Content-Length: " + buffer.length + "\r\n");
pw.print("\r\n");
pw.flush();
os.write(buffer);
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
soc.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
-- 编辑--
我已经为 eClipse 上传了一个名为 SSLTest 的示例应用程序项目,它演示了该问题:
http://code.google.com/p/android/issues/detail?id=59536
-- 更新--
好消息:今天发现了上面报告的 Android 问题,并提交了适当的提交来修复内存泄漏。有关详细信息,请参阅上面的链接。
最佳答案
我想这将是一笔可观的时间投资,但我看到 Valgrind 已被移植到 Android。您可以尝试启动并运行它。当然,如果您发现存在内部内存泄漏,除了尝试在未来的 Android 版本中修复该错误之外,您无能为力。
作为一种解决方法,您可以使您的应用程序多进程并将 https 服务放在一个单独的进程中。这样您就可以定期重新启动它,避免 OOM。您可能还必须有第三个进程,只接受端口 443 连接并将它们传递给 https worker - 以避免重新启动 https worker 时出现微小的中断。
这听起来也需要大量的时间投入 :) 但它可能会成功地避免这个问题。
--- 编辑:更多细节 ---
是的,如果您有一个拥有自己 UI 的主应用程序、一个用于处理 SSL 的工作进程和一个用于接受 SSL 请求的工作进程(正如您所说的可能不能是 443),那么在您的正常操作之上Activity 类,您将有两个 Service 类,并且 list 会将它们放在不同的进程中。
处理 SSL 进程:与其等待 OOM 使服务崩溃,服务可以监控自己的 Debug.getNativeHeapSize(),并在其增加过多时显式重启服务。要么这样,要么在每 100 个左右的请求后自动重启。
处理监听套接字进程:此服务只会监听您选择的 TCP 端口并将原始数据传递给 SSL 进程。这一点需要考虑,但最明显的解决方案是让 SSL 进程在不同的本地端口 X 上监听(或在选择的不同端口之间切换),监听套接字进程会将数据转发到端口 X。原因让监听套接字进程优雅地处理 X 关闭的可能性 - 就像你重新启动它时一样。
如果您的要求允许偶尔出现小型中断,我将只处理 SSL 进程,并跳过监听套接字进程,那么这是一个相对简单的解决方案 - 与您通常所做的没有什么不同。是监听套接字进程增加了解决方案的复杂性...
关于Android 的 SSLServerSocket 导致 App 中的原生内存增加,OOM,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18484514/
我正在尝试使用 Spark 从 Cassandra 读取数据。 DataFrame rdf = sqlContext.read().option("keyspace", "readypulse
这是代码: void i_log_ (int error, const char * file, int line, const char * fmt, ...) { /* Get erro
我必须调试一个严重依赖 Gtk 的程序。问题是由于某些原因,在使用 GtkWindow 对象时开始出现许多运行时警告。问题是,即使 Gtk 提示严重错误,它也不会因这些错误而中止。我没有代码库的更改历
我正在尝试从已有效编译和链接的程序中检索二进制文件。我已经通过 GL_PROGRAM_BINARY_LENGTH 收到了它的长度。该文档说有两个实例可能会发生 GL_INVALID_OPERATION
我有一个托管在 Azure 环境中的服务。我正在使用控制台应用程序使用该服务。这样做时,我得到了异常: "The requested service, 'http://xxxx-d.yyyy.be/S
我有以下代码,它被 SEGV 信号杀死。使用调试器表明它被 main() 中的第一个 sem_init() 杀死。如果我注释掉第一个 sem_init() ,第二个会导致同样的问题。我试图弄清楚是什么
目前我正在编写一个应用程序(目标 iOS 6,启用 ARC),它使用 JSON 进行数据传输,使用核心数据进行持久存储。 JSON 数据由 PHP 脚本通过 json_encode 从 MySQL 数
我对 Xamarin.Forms 还是很陌生。我在出现的主页上有一个非常简单的功能 async public Task BaseAppearing() { if (UserID
这是我的代码的简化版本。 public class MainActivity extends ActionBarActivity { private ArrayList entry = new Arr
我想弄明白为什么我的两个 Java 库很难很好地协同工作。这是场景: 库 1 有一个类 A,其构造函数如下: public A(Object obj) { /* boilerplate */ } 在以
如果网站不需要身份验证,我的代码可以正常工作,如果需要,则在打印“已创建凭据”后会立即出现 EXC_BAD_ACCESS 错误。我不会发布任何内容,并且此代码是直接从文档中复制的 - 知道出了什么问题
我在使用 NSArray 填充 UITableView 时遇到问题。我确信我正在做一些愚蠢的事情,但我无法弄清楚。当我尝试进行简单的计数时,我得到了 EXC_BAD_ACCESS,我知道这是因为我试图
我在 UITableViewCell 上有一个 UITextField,在另一个单元格上有一个按钮。 我单击 UITextField(出现键盘)。 UITextField 调用了以下方法: - (BO
我有一个应用程序出现间歇性崩溃。崩溃日志显示了一个堆栈跟踪,这对我来说很难破译,因此希望其他人看到了这一点并能为我指出正确的方向。 基本上,应用程序在启动时执行反向地理编码请求,以在标签中显示用户的位
我开发了一个 CGImage,当程序使用以下命令将其显示在屏幕上时它工作正常: [output_view.layer performSelectorOnMainThread:@selector(set
我正在使用新的 EncryptedSharedPreferences以谷歌推荐的方式上课: private fun securePrefs(context: Context): SharedPrefe
我有一个中继器,里面有一些控件,其中一个是文本框。我正在尝试使用 jquery 获取文本框,我的代码如下所示: $("#").click(function (event) {}); 但我总是得到 nu
在以下场景中观察到 TTS 初始化错误,太随机了。 已安装 TTS 引擎,存在语音集,并且可以从辅助功能选项中播放示例 tts。 TTS 初始化在之前初始化和播放的同一设备上随机失败。 在不同的设备(
maven pom.xml org.openjdk.jol jol-core 0.10 Java 类: public class MyObjectData { pr
在不担心冲突的情况下,可以使用 MD5 作为哈希值,字符串长度最多为多少? 这可能是通过为特定字符集中的每个可能的字符串生成 MD5 哈希来计算的,长度不断增加,直到哈希第二次出现(冲突)。没有冲突的
我是一名优秀的程序员,十分优秀!