最近项目中,遇到一个报错:

java.lang.OutOfMemoryError: unable to create new native thread

报错的场景是:一个消息的群发,群里总共有50多个人,一群发 tomcat 就崩溃了,查看日志报上面的错误。

这个错误的基本情况是,没有内存来创建新的本地线程了,因为java中创建的线程,最终是要对应到本地线程。基本的解决方法,是减小:xss 的数值,页就是减小 java 线程stack 占用内存的大小,一般可以改小到 128k。

关于这个错误网上有一篇博文可以参考:http://sesame.iteye.com/blog/622670

http://stackoverflow.com/ 上也有很多分析好答案。

You have a chance to face the java.lang.OutOfMemoryError: Unable to create new native thread whenever the JVM asks for a new thread from the OS. Whenever the underlying OS cannot allocate a new native thread, this OutOfMemoryError will be thrown. The exact limit for native threads is very platform-dependent thus we recommend to find out those limits by running a test similar to the below example.

重现错误:

	public static void main(String[] args) {
LongAdder adder = new LongAdder();
while(true){
new Thread(new Runnable(){
public void run() {
try {
adder.increment();
Thread.sleep(10000000);
} catch(InterruptedException e) { }
}
}).start();
System.out.println(adder.longValue());
} }

2331
2331
2331
2333
2335
2340

...
2355

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:714)
可以看到我电脑大概可以创建2355个线程,在创建,内存就不够了,爆出错误。

虽然通过 修改 xss 以及在群发消息时,每一条消息之间增加几秒的间隔,这样把问题解决了。但是问题的本质似乎还是没有解决。

因为 发送消息使用的 http 协议发送的,原代码中 每发送一条消息,就创建一条 HttpUrlConnection,所以这里应该可以进行优化,

在官方文档中找到了相关文档:http://docs.oracle.com/javase/8/docs/technotes/guides/net/http-keepalive.html

What is HTTP Persistent Connections?

HTTP persistent connections, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using the same TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new one for every single request/response pair. Using persistent connections is very important for improving HTTP performance.

There are several advantages of using persistent connections, including:

  • Network friendly. Less network traffic due to fewer setting up and tearing down of TCP connections.
  • Reduced latency on subsequent request. Due to avoidance of initial TCP handshake
  • Long lasting connections allowing TCP sufficient time to determine the congestion state of the network, thus to react appropriately.

The advantages are even more obvious with HTTPS or HTTP over SSL/TLS. There, persistent connections may reduce the number of costly SSL/TLS handshake to establish security associations, in addition to the initial TCP connection set up.

In HTTP/1.1, persistent connections are the default behavior of any connection. That is, unless otherwise indicated, the client SHOULD assume that the server will maintain a persistent connection, even after error responses from the server. However, the protocol provides means for a client and a server to signal the closing of a TCP connection.

What makes a connection reusable?

Since TCP by its nature is a stream based protocol, in order to reuse an existing connection, the HTTP protocol has to have a way to indicate the end of the previous response and the beginning of the next one. Thus, it is required that all messages on the connection MUST have a self-defined message length (i.e., one not defined by closure of the connection). Self demarcation is achieved by either setting the Content-Length header, or in the case of chunked transfer encoded entity body, each chunk starts with a size, and the response body ends with a special last chunk.

What happens if there are proxy servers in between?

Since persistent connections applies to only one transport link, it is important that proxy servers correctly signal persistent/or-non-persistent connections separately with its clients and the origin servers (or to other proxy servers). From a HTTP client or server's perspective, as far as persistence connection is concerned, the presence or absence of proxy servers is transparent.

What does the current JDK do for Keep-Alive?

The JDK supports both HTTP/1.1 and HTTP/1.0 persistent connections.

When the application finishes reading the response body or when the application calls close() on the InputStream returned by URLConnection.getInputStream(), the JDK's HTTP protocol handler will try to clean up the connection and if successful, put the connection into a connection cache for reuse by future HTTP requests.

The support for HTTP keep-Alive is done transparently. However, it can be controlled by system properties http.keepAlive, and http.maxConnections, as well as by HTTP/1.1 specified request and response headers.

The system properties that control the behavior of Keep-Alive are:

http.keepAlive=<boolean>
default: true

Indicates if keep alive (persistent) connections should be supported.

http.maxConnections=<int>
default: 5

Indicates the maximum number of connections per destination to be kept alive at any given time

HTTP header that influences connection persistence is:

Connection: close

If the "Connection" header is specified with the value "close" in either the request or the response header fields, it indicates that the connection should not be considered 'persistent' after the current request/response is complete.

The current implementation doesn't buffer the response body. Which means that the application has to finish reading the response body or call close() to abandon the rest of the response body, in order for that connection to be reused. Furthermore, current implementation will not try block-reading when cleaning up the connection, meaning if the whole response body is not available, the connection will not be reused.

What's new in JDK 5?

When the application encounters a HTTP 400 or 500 response, it may ignore the IOException and then may issue another HTTP request. In this case, the underlying TCP connection won't be Kept-Alive because the response body is still there to be consumed, so the socket connection is not cleared, therefore not available for reuse. What the application needs to do is callHttpURLConnection.getErrorStream() after catching the IOException , read the response body, then close the stream. However, some existing applications are not doing this. As a result, they do not benefit from persistent connections. To address this problem, we have introduced a workaround.

The workaround involves buffering the response body if the response is >=400, up to a certain amount and within a time limit, thus freeing up the underlying socket connection for reuse. The rationale behind this is that when the server responds with a >=400 error (client error or server error. One example is "404: File Not Found" error), the server usually sends a small response body to explain whom to contact and what to do to recover.

Several new Oracle JDK implementation specific properties are introduced to help clean up the connections after error response from the server.

The major one is:

sun.net.http.errorstream.enableBuffering=<boolean>
default: false

With the above system property set to true (default is false), when the response code is >=400, the HTTP handler will try to buffer the response body. Thus freeing up the underlying socket connection for reuse. Thus, even if the application doesn't call getErrorStream(), read the response body, and then call close(), the underlying socket connection may still be kept-alive and reused.

The following two system properties provide further control to the error stream buffering behavior:

sun.net.http.errorstream.timeout=<int> in millisecond
default: 300 millisecond

sun.net.http.errorstream.bufferSize=<int> in bytes
default: 4096 bytes

What can you do to help with Keep-Alive?

Do not abandon a connection by ignoring the response body. Doing so may results in idle TCP connections. That needs to be garbage collected when they are no longer referenced.

If getInputStream() successfully returns, read the entire response body.

When calling getInputStream() from HttpURLConnection, if an IOException occurs, catch the exception and call getErrorStream() to get the response body (if there is any).

Reading the response body cleans up the connection even if you are not interested in the response content itself. But if the response body is long and you are not interested in the rest of it after seeing the beginning, you can close the InputStream. But you need to be aware that more data could be on its way. Thus the connection may not be cleared for reuse.

Here's a code example that complies to the above recommendation:

try {
URL a = new URL(args[0]);
URLConnection urlc = a.openConnection();
is = conn.getInputStream();
int ret = 0;
while ((ret = is.read(buf)) > 0) {
processBuf(buf);
}
// close the inputstream
is.close();
} catch (IOException e) {
try {
respCode = ((HttpURLConnection)conn).getResponseCode();
es = ((HttpURLConnection)conn).getErrorStream();
int ret = 0;
// read the response body
while ((ret = es.read(buf)) > 0) {
processBuf(buf);
}
// close the errorstream
es.close();
} catch(IOException ex) {
// deal with the exception
}
}

If you know ahead of time that you won't be interested in the response body, you should issue a HEAD request instead of a GET request. For example when you are only interested in the meta info of the web resource or when testing for its validity, accessibility and recent modification. Here's a code snippet:

URL a = new URL(args[0]);
URLConnection urlc = a.openConnection();
HttpURLConnection httpc = (HttpURLConnection)urlc;
// only interested in the length of the resource
httpc.setRequestMethod("HEAD");
int len = httpc.getContentLength();

Changes in JDK 6

Prior to JDK 6, if an application closes a HTTP InputStream when more than a small amount of data remains to be read, then the connection had to be closed, rather than being cached. Now inJDK 6, the behavior is to read up to 512 Kbytes off the connection in a background thread, thus allowing the connection to be reused. The exact amount of data which may be read is configurable through the http.KeepAlive.remainingData system property.

根据这篇文章的说明,将实行改成使用 keepAlive 的httpUrlConnection来实现,性能得到了很大的提升。
注意,一定要 server 端支持 Keepalive,判断方法:看 http 头部 Connection:keepalive 是否是这个值。
代码:
public static void main(String[] args) {
try {
URL obj = new URL("www.xxx.com");
URLConnection conn = obj.openConnection();
Map<String, List<String>> map = conn.getHeaderFields(); System.out.println("显示响应Header信息...\n"); for (Map.Entry<String, List<String>> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() +
" ,Value : " + entry.getValue());
} // Key : Connection ,Value : [keep-alive] System.out.println("\n使用key获得响应Header信息 \n");
List<String> server = map.get("Server"); if (server == null) {
System.out.println("Key 'Server' is not found!");
} else {
for (String values : server) {
System.out.println(values);
}
} } catch (Exception e) {
e.printStackTrace();
}
}

大量的HttpUrlConnection、大量的TCP连接,肯定会占用系统内存。改成共用一条HttpUrlConnection/TCP连接,内存占用减少很多。  

 

  

HttpsURLConnection 利用keepAlive特性进行优化一例的更多相关文章

  1. 使AJAX调用尽可能利用缓存特性

    优化网站设计(十四):使AJAX调用尽可能利用缓存特性 前言 网站设计的优化是一个很大的话题,有一些通用的原则,也有针对不同开发平台的一些建议.这方面的研究一直没有停止过,我在不同的场合也分享过这样的 ...

  2. MySQL · 特性分析 · 优化器 MRR & BKA【转】

    MySQL · 特性分析 · 优化器 MRR & BKA 上一篇文章咱们对 ICP 进行了一次全面的分析,本篇文章小编继续为大家分析优化器的另外两个选项: MRR & batched_ ...

  3. MySQL派生表(derived)优化一例

    1.什么是派生表derived 关键字:子查询–>在From后where前的子查询 mysql; +----+-------------+------------+------+-------- ...

  4. 树形查询SQL优化一例

    上周五一哥们发了条SQL,让我看看,代码如下: SELECT COUNT(1) FROM (select m.sheet_id from cpm_main_sheet_history m, cpm_s ...

  5. 利用python自动生成verilog模块例化模板

    一.前言 初入职场,一直忙着熟悉工作,就没什么时间更新博客.今天受“利奇马”的影响,只好宅在家中,写写技术文章.芯片设计规模日益庞大,编写脚本成了芯片开发人员必要的软技能.模块端口动不动就几十上百个, ...

  6. Qualcomm_Mobile_OpenCL.pdf 翻译-9-OpenCL优化用例的学习

    在这一章中,将会用一些例子来展示如何使用之前章节中讨论的技术来进行优化.除了一些小的简单代码片段的展示外,还有两个熟知的图像滤波处理,Epsilon滤波和Sobel滤波,将会使用之前章节中讨论的方法进 ...

  7. 利用keepalive+mysql replication 实现数据库的高可用

    利用keepalive+mysql replication 实现数据库的高可用 http://www.xuchanggang.cn/archives/866.html

  8. 编写高质量代码改善C#程序的157个建议——建议55:利用定制特性减少可序列化的字段

    建议55:利用定制特性减少可序列化的字段 特性(attribute)可以声明式地为代码中的目标元素添加注释.运行时可以通过查询这些托管块中的元数据信息,达到改变目标元素运行时行为的目的.System. ...

  9. 利用闭包特性改写addEventListener的回调函数

    var numClicks = 0; document.addEventListener("click",function(){ alert( ++numClicks); },fa ...

随机推荐

  1. ABP框架 - 介绍

    文档目录 本节内容: 简介 一个快速示例 其它特性 启动模板 如何使用 简介 我们总是对不同的需求开发不同的应用.但至少在某些层面上,一次又一次地重复实现通用的和类似的功能.如:授权,验证,异常处理, ...

  2. php缓冲区详解

    什么是缓冲区(buffer)? 简单而言,缓冲区的作用就是,把输入或者输出的内容先放进内存,而不显示或者读取.至于为什么要有缓冲区,这是一个很广泛的问题,如果有兴趣,可以在网山找下资料. 其实缓冲区最 ...

  3. Linux 与 Linux Windows 文件共享 小知识

    Linux 与 Linux Windows 文件共享   前提说明:windows主机信息:192.168.1.100 帐号:abc 密码:123 共享文件夹:sharelinux主机信息:192.1 ...

  4. Kafka 文档引言

    原文地址:https://kafka.apache.org/documentation.html#semantics 1.开始 1.1 引言 Kafka是一个分布式,分区队列,冗余备份的消息存储服务. ...

  5. 从零开始编写自己的C#框架——框架学习补充说明

    非常感谢轩辕公子提出了对本框架的看法与意见,所以这里也将回复贴出来,让大家都了解一下 本系列的快速开发指的是,框架构建完毕后,在这个基础上开发新功能非常快捷方便,基本不用写太多代码就可以在短时间内完成 ...

  6. 《转载》PAT 习题

    博客出处:http://blog.csdn.net/zhoufenqin/article/details/50497791 题目出处:https://www.patest.cn/contests/pa ...

  7. 读书笔记--SQL必知必会08--使用函数处理数据

    8.1 函数 每个DBMS都有特定的函数.事实上,只有少数的几个函数被所有主要DBMS同时支持. 实现同一功能的函数,在不同的DBMS中的名称和语法极有可能不同,也就是说SQL函数不可移植的. 可移植 ...

  8. iOS关于模块化开发解决方案(纯干货)

    关于iOS模块化开发解决方案网上也有一些介绍,但真正落实在在具体的实例却很少看到,计划编写系统文章来介绍关于我对模块化解决方案的理解,里面会有包含到一些关于解耦.路由.封装.私有Pod管理等内容:并编 ...

  9. C#开发微信门户及应用(15)-微信菜单增加扫一扫、发图片、发地理位置功能

    前面介绍了很多篇关于使用C#开发微信门户及应用的文章,基本上把当时微信能做的接口都封装差不多了,微信框架也积累了不少模块和用户,最近发现微信公众平台增加了不少内容,特别是在自定义菜单里面增加了扫一扫. ...

  10. Asp.net 面向接口可扩展框架之业务规则引擎扩展组件

    随着面向接口可扩展框架的继续开发,有些功能开发出现了"瓶颈",有太多的东西要写死才好做.但写死的代码扩展性是非常的不好,迷茫中寻找出入... 进而想到我以前开发的好几个项目,都已有 ...