[转]JVM内存溢出的几种方式比较
转载自:https://github.com/pzxwhc/MineKnowContainer/issues/25
包括:
1. 栈溢出(StackOverflowError)
2. 堆溢出(OutOfMemoryError:java heap space)
3. 永久代溢出(OutOfMemoryError: PermGen space)
4. OutOfMemoryError:unable to create native thread
Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆内存分为了两部:新生代,老年代。在堆内存之外,还有永久代,其中永久代实现了规范中规定的方法区,而内存模型中不同的部分都会出现相应的OOM错误,接下来我们就分开来讨论一下。
栈溢出(StackOverflowError)
栈溢出抛出StackOverflowError错误,出现此种情况是因为方法运行的时候栈的深度超过了虚拟机容许的最大深度所致。出现这种情况,一般情况下是程序错误所致的,比如写了一个死递归,就有可能造成此种情况。 下面我们通过一段代码来模拟一下此种情况的内存溢出。
import java.util.*;
import java.lang.*;
public class OOMTest{
public void stackOverFlowMethod(){
stackOverFlowMethod();
}
public static void main(String... args){
OOMTest oom = new OOMTest();
oom.stackOverFlowMethod();
}
}
运行上面的代码,会抛出如下的异常:
Exception in thread "main" java.lang.StackOverflowError
at OOMTest.stackOverFlowMethod(OOMTest.java:6)
对于栈内存溢出,根据《Java 虚拟机规范》中文版:如果线程请求的栈容量超过栈允许的最大容量的话,Java 虚拟机将抛出一个StackOverflow异常;如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。
堆溢出(OutOfMemoryError:java heap space)
堆内存溢出的时候,虚拟机会抛出java.lang.OutOfMemoryError:java heap space,出现此种情况的时候,我们需要根据内存溢出的时候产生的dump文件来具体分析(需要增加-XX:+HeapDumpOnOutOfMemoryErrorjvm启动参数)。出现此种问题的时候有可能是内存泄露,也有可能是内存溢出了。
- 如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。
- 如果出现了内存溢出问题,这往往是程序本身需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。下面我们通过如下的代码来演示一下此种情况的溢出:
import java.util.*;
import java.lang.*;
public class OOMTest{
public static void main(String... args){
List<byte[]> buffer = new ArrayList<byte[]>();
buffer.add(new byte[10*1024*1024]);
}
}
我们通过如下的命令运行上面的代码:
java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest
程序输出如下的信息:
[GC 1180K->366K(19456K), 0.0037311 secs]
[Full GC 366K->330K(19456K), 0.0098740 secs]
[Full GC 330K->292K(19456K), 0.0090244 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at OOMTest.main(OOMTest.java:7)
从运行结果可以看出,JVM进行了一次Minor gc和两次的Major gc,从Major gc的输出可以看出,gc以后old区使用率为134K,而字节数组为10M,加起来大于了old generation的空间,所以抛出了异常,如果调整-Xms21M,-Xmx21M,那么就不会触发gc操作也不会出现异常了。
通过上面的实验其实也从侧面验证了一个结论:当对象大于新生代剩余内存的时候,将直接放入老年代,当老年代剩余内存还是无法放下的时候,触发垃圾收集,收集后还是不能放下就会抛出内存溢出异常了。
持久带溢出(OutOfMemoryError: PermGen space)
我们知道Hotspot jvm通过持久带实现了Java虚拟机规范中的方法区,而运行时的常量池就是保存在方法区中的,因此持久带溢出有可能是运行时常量池溢出,也有可能是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。
当持久带溢出的时候抛出java.lang.OutOfMemoryError: PermGen space。可能在如下几种场景下出现:
- 使用一些应用服务器的热部署的时候,我们就会遇到热部署几次以后发现内存溢出了,这种情况就是因为每次热部署的后,原来的class没有被卸载掉。
- 如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。
- 一些第三方框架,比如spring,hibernate都通过字节码生成技术(比如CGLib)来实现一些增强的功能,这种情况可能需要更大的方法区来存储动态生成的Class文件。
我们知道Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么我们就可以通过String.intern方法来模拟一下运行时常量区的溢出.下面我们通过如下的代码来模拟此种情况:
import java.util.*;
import java.lang.*;
public class OOMTest{
public static void main(String... args){
List<String> list = new ArrayList<String>();
while(true){
list.add(UUID.randomUUID().toString().intern());
}
}
}
我们通过如下的命令运行上面代码:java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest
运行后的输入如下图所示:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at OOMTest.main(OOMTest.java:8)
通过上面的代码,我们成功模拟了运行时常量池溢出的情况,从输出中的PermGen space可以看出确实是持久带发生了溢出,这也验证了,我们前面说的Hotspot jvm通过持久带来实现方法区的说法。
OutOfMemoryError:unable to create native thread
最后我们在来看看java.lang.OutOfMemoryError:unable to create natvie thread这种错误。 出现这种情况的时候,一般是下面两种情况导致的:
1. 程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit -u来查看此限制。
2. 给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。
我们都知道操作系统对每个进程的内存是有限制的,我们启动Jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。线程栈总可用内存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存
通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。
总结
- 栈内存溢出:程序所要求的栈深度过大导致。
- 堆内存溢出: 分清 内存泄露还是 内存容量不足。泄露则看对象如何被 GC Root 引用。不足则通过 调大 -Xms,-Xmx参数。
- 持久带内存溢出:Class对象未被释放,Class对象占用信息过多,有过多的Class对象。
- 无法创建本地线程:总容量不变,堆内存,非堆内存设置过大,会导致能给线程的内存不足。
补充:阿里巴巴内存溢出面试题
下面哪种情况会导致持久区jvm堆内存溢出():
A. 循环上万次的字符串处理
B. 在一段代码内申请上百M甚至上G的内存
C. 使用CGLib技术直接操作字节码运行,生成大量的动态类
D. 不断创建对象
解答:AC
解析:AC是持久带,B直接内存也就是堆外内存,D堆内存。
[转]JVM内存溢出的几种方式比较的更多相关文章
- windows下配置tomcat服务器的jvm内存大小的两种方式
难得遇到一次java堆内存溢出(心里想着,终于可以来一次jvm性能优化了$$) 先看下报错信息, java.lang.OutOfMemoryError: GC overhead limit excee ...
- jvm内存溢出的三种情况以及解决办法
1 前言相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深 ...
- 5种JVM垃圾收集器特点和8种JVM内存溢出原因
先来看看5种JVM垃圾收集器特点 一.常见垃圾收集器 现在常见的垃圾收集器有如下几种: 新生代收集器: Serial ParNew Parallel Scavenge 老年代收集器: Serial O ...
- jvm内存溢出问题
Java内存溢出详解 一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryError: Java heap space ----JVM Heap(堆)溢出 J ...
- 定位JVM内存溢出问题思路总结
JVM的内存溢出问题,是个常见而有时候有非常难以定位的问题.定位内存溢出问题常见方法有很多,但是其实很多情况下可供你选择的有效手段非常有限.很多方法在一些实际场景下没有实用价值.这里总结下我的一些定位 ...
- 解决JVM内存溢出问题
今天遇到了一个问题,当我在增加配置文件(*.xml)内容的时候,重新启动tomcat6时,控制台报错:java.lang.StackOverflowError: 即,栈溢出错误. 内存溢出,即程序运行 ...
- JVM 内存溢出 实战 (史上最全)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- Tomcat中JVM内存溢出及合理配置及maxThreads如何配置(转)
来源:http://www.tot.name/html/20150530/20150530102930.htm Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚 ...
- Tomcat中JVM内存溢出及合理配置
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...
随机推荐
- vue组件属性中字符串如何拼接变量?
不得不说,对于水平只有jquery的vue初学者来说,vue的图片加载实现确实挺坑的,在文档中也没有看到说明.经过百度之后终于知道了什么情况. 首先: 这样是没问题的: <img src=&qu ...
- [Functional Programming ADT] Debug a Functional JavaScript composeK Flow
When using ADTs in our code base, it can be difficult to use common debugging tools like watches and ...
- 云计算之路-试用Azure:数据库备份压缩文件在虚拟机上的恢复速度测试
测试环境:Windows Azure上海机房,虚拟机配置为大型(四核,7 GB 内存),磁盘情况见下图. 数据库备份压缩文件大于为12.0 GB (12,914,327,552 bytes),放置于T ...
- 云计算之路-阿里云上:启用Windows虚拟内存引发的CPU 100%故障
今天上午11:35~11:40左右,由于负载均衡中的两台云服务器CPU占用突然飚至100%,造成网站5分钟左右不能正常访问,请大家带来了麻烦,请谅解! (上图中红色曲线表示CPU占用) 经过分析,我们 ...
- Linux中查看jdk安装目录、Linux卸载jdk、rpm命令、rm命令参数
一.查看jdk安装目录 [root@node001 ~]# whereis java java: /usr/bin/java /usr/local/java #java执行路径 [root@node0 ...
- PHP和数据访问之(插入。删除。和更新数据)
插入: <?php $conn=@new mysqli('localhost','root','123','mytestdb'); $q_str=<<<EOM insert i ...
- 大并发server架构 && 大型站点架构演变
server的三条要求: 高性能:对于大量请求,及时高速的响应 高可用:7*24 不间断,出现问题自己主动转移.这叫fail over(故障转移) 伸缩性:使用跨机器的通信(TCP) 另外不论什么网络 ...
- JDBC:数据库操作:事务
事务特征:原子性,一致性,独立性,持久性. 要想操作事务,必须按照以下步骤完成. 1,取消掉自动提交(SET AUTOCOMMIT=0):每次执行数据库更新的时候实际上发出SQL命令之后就已经提交上去 ...
- 应用zip压缩的javascript以及Egret H5游戏实战
代码地址如下:http://www.demodashi.com/demo/11039.html 主要起因是策划对最快进入登录界面有硬性要求(3秒),那么最开始加载的文件越小越好.对H5的游戏程序进行压 ...
- nginx 配置一个文件下载服务
cat openvpn.conf server { listen ; server_name localhost; location / { root /home/openvpn/client_fil ...