深入理解jvm阅读笔记-内存溢出小结

reload reload     2022-10-04     404

关键词:

JAVA系统除了程序计数器和虚拟机内存之外的其它几个内存区域都有发生OutOfMemory(OOM)的可能。堆,栈,方法区,静态常量池,直接内存,都是可能的。

1.Java堆溢出

Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在堆达到最大容量的限制后就会产生内存溢出异常。

-Xms -Xmx 参数可以设置Java堆的大小(实际使用中一般Xms和Xmx的大小一致,放置堆自动扩展),通过参数-XX:+HeapDumpOnOutOfMemoryError让虚拟机在出现内存溢出的异常时Dump出当前的内存堆转储快照以便事后进行分析。

当Java出现堆溢出的时候,异常的堆栈信息为“java.lang.OutOfMemberError” 后会跟着进一步提示“java heap space”

下面有一个堆溢出的OOM例子:

/**

 * -verbose:gc -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError

 *  -XX:+PrintGCDetails -XX:SurvivorRatio=8

 * @author scl

 *

 */

public class HeapOOM {

      

       static class OOMObject {

             

       }

      

       public static void main(String[] args) {

              List<OOMObject> list = new ArrayList<OOMObject>();

              while(true){

                     list.add(new OOMObject());

              }

       }

}     

 

下方为执行结果:

可以看到出现了一个堆溢出,并产生了一个dump

要解决这个区域的异常需要借助内存映像分析工具对转储快照进行分析,确认内存中的对象是否是必要的。也就是确认到底出现了内存泄露(Member Leak)还是内存溢出(Member Overflow)。

如果存在内存泄露,可以通过内存分析工具查看泄露的对象到GC Roots的引用.--暂时不理解如何查看引用

如下,可以推测出16M的空间被一个Object持有,对应的该对象内部都是OOMObject构成的,很自然的联想到List<OOMObject> list = new ArrayList<OOMObject>();对象.

如果是内存溢出那么就需要检查-Xms 和-Xmx大小,本机物理内存大小,或者其他调优。

2.虚拟机栈和本地方法栈溢出

栈容量由-Xss参数设定,以下是测试案例:

/**

 * -verbose:gc -Xms20M -Xmx20M -Xss128K

 *  -XX:+PrintGCDetails -XX:SurvivorRatio=8

 * @author scl

 *

 */

public class JavaVMStackSOF {

       private int stackInteger = 1;

      

       public void stackLeak(){

              stackInteger++;

              stackLeak();

       }

      

       public static void main(String[] args) throws  Throwable{

              JavaVMStackSOF oom = new JavaVMStackSOF();

              try{

                     oom.stackLeak();

              }catch (Throwable e) {

                     // TODO: handle exception

                     System.out.println("stack length :"+oom.stackInteger);

                     throw e;

              }

             

       }

}

 

 

下方是结果截图:

这里顺便提一下本来我在这里异常捕获的时候使用的是Exception ,结果不会打印出stack length 所以表示这个StackOverflowError不是Exception。而使用Throwable即可捕获并打印出栈深度。

这里测试使用的是单线程的情况,如果是多线程的情况下,每个栈分配的内存越大,反而更容易产生内存溢出异常。原因为栈的大小为:操作系统内存-Xmx(最大堆容量)-MaxPermSize(最大方法区容量)-程序计数器(可以忽略不计)。所以在操作系统内存恒定的情况下,每个线程分配的栈容量越大,可以创建的线程数就越少,对应的创建一个新的线程就越容易发生内存溢出。当系统内存溢出又无法减少线程数或者加大总内存的情况下可以尝试减少栈的-Xss值创建新的线程。

/**

 * -verbose:gc -Xms20M -Xmx20M -Xss2M

 * @author scl

 *

 */

public class JavaVMStackOOM {

       private void dontStop(){

              while(true){

                    

              }

       }

      

       public void stackLeakByThread(){

              while(true){

                     Thread t = new Thread(new Runnable() {

                           

                            @Override

                            public void run() {

                                   // TODO Auto-generated method stub

                                   dontStop();

                            }

                     });

                     t.start();

              }

       }

      

       public static void main(String[] args) {

              JavaVMStackOOM oom = new JavaVMStackOOM();

              oom.stackLeakByThread();

       }

}

 

以上代码执行具有风险,会造成操作系统假死,如果想要尝试请先保存当前所有文件。

3.方法区和内存常量池溢出

由于内存常量池包含在方法区内部,所以可以使用-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制内存常量池的大小。

/**

 * -XX:PermSize=10M -XX:MaxPermSize=10M

 * @author scl

 *

 */

public class RuntimeConstantPoolOOM {

       public static void main(String[] args) {

              List<String> list = new ArrayList<String>();

              int i = 0;

              while(true){

                     list.add(String.valueOf(i++).intern());

              }

       }

}

 

异常信息如下

Sting. Intern方法是一个Native方法,如果字符串常量池中已经包含一个等于该String对象的字符串,则返回代表池中这个字符串的String对象,否则将此String对象添加到常量池中,并返回此String对象的引用.此处OOM后跟着PermGen space也证明了HotSpot的常量池属于方法区的一部分

4.本机直接内存溢出--没有完成测试

DircetMemory容量可以通过-XX:MaxDirectMemorySize指定,如果不指定则与JAVA堆的最大值(-Xmx)一样。

DircetMemory造成的内存溢出,一个明显的特征是Heap Dump文件会看不到任何明显的异常如果发现OOM之后的dump文件很小,那么可以考虑是不是这方面的问题。

 

以上代码及MemoryAnalyzer均上传至百度网盘 链接: https://pan.baidu.com/s/1i5eUrfn 密码: qauf。

深入理解jvm——java内存区域与内存溢出异常

本文为《深入理解Java虚拟机》第二章内容的学习笔记,部分内容经过二次加工。若对相关知识感兴趣,推荐购书深入阅读。若认为文章涉嫌侵权,请联系作者及时删除。本作品采用知识共享署名-非商业性使用-相同方... 查看详情

《深入理解jvm3ed》读书笔记(代码片段)

前言刷豆瓣看到《深入理解JVM》出第三版了,遂买之更新JVM知识,本文为笔记,仅供个人Reviewch02.Java内存区域与内存溢出2.1运行时数据区域参考:JVM规范,MemoriesofaJavaRuntime堆:JVM启动时按-Xmx,-Xms大小创建... 查看详情

深入理解jvm之知识体系(代码片段)

一.JVM知识体系组成1.JVM内存区域(1)组成部分(2)各部分的作用2.JVM内存溢出(1)内存溢出(2)栈溢出(3)内存区域与溢出的关联3.垃圾回收机制(1)对象存活状态判断(2)垃圾收集算法(3)垃圾收集器(4)垃圾回收过程4.类加载(1)类的生命周期(2)类... 查看详情

深入理解jvm

2.jvm性能调优都做了什么3.介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明内存溢出和合理分配:http://blog.csdn.net/ye1992/article/details/9344807http://flychao88.iteye.com/blog/2226205http://www.itzhai.com/jvm-note-automatic-memory-mana 查看详情

java内存区域——深入理解jvm读书笔记

本内容由《深入理解java虚拟机》的部分读书笔记整理而成,本读者计划连载。通过如下图和文字介绍来了解几个运行时数据区的概念。方法区:它是各个线程共享的区域,用于内存已被VM加载的类信息、常量、静态变量、即时编... 查看详情

深入理解java虚拟机:jvm内存管理与垃圾收集理论(代码片段)

文章目录阅读的疑问???第二部分自动内存管理第2章Java内存区域与内存溢出异常1.程序计数器2.Java虚拟机栈3.本地方法栈4.Java堆5.方法区6.直接内存(我理解就是堆外内存吧)HotSpot虚拟机对象探秘1.对象的创... 查看详情

深入理解jvm

一、内存管理1、运行时的内存区域线程私有:虚拟机栈、本地方法栈、程序计数器线程共享:堆、方法区2、各个内存区域可能抛出的异常栈1、当单线程时,栈的深度太大,会发生StackOverflowError,比如无穷的递归调用。2、当多... 查看详情

《深入理解jvm虚拟机》读书笔记

前言:《深入理解JVM虚拟机》是JAVA的经典著作之一,因为内容更偏向底层,比较枯燥难啃,所以之前一直没有好好的阅读过。最近因为刚好有空,又有了新目标。所以打算和《构架师的12项修炼》一起看,这样荤素搭配,吃饭不... 查看详情

深入理解jvm虚拟机读书笔记——内存模型与线程(代码片段)

注:本文参考自周志明老师的著作《深入理解Java虚拟机(第3版)》,相关电子书可以关注WX公众号,回复001获取。1.Java内存模型JMM概述:Java内存模型指的是JMM,而不是运行时数据区哦~Java语言为了保证... 查看详情

深入理解jvm虚拟机读书笔记——内存模型与线程(代码片段)

注:本文参考自周志明老师的著作《深入理解Java虚拟机(第3版)》,相关电子书可以关注WX公众号,回复001获取。1.Java内存模型JMM概述:Java内存模型指的是JMM,而不是运行时数据区哦~Java语言为了保证... 查看详情

深入理解java虚拟机笔记1:oom实战(代码片段)

通过代码模拟Java虚拟机规范中描述的各个运行时区域内存溢出的场景。1.Java堆溢出2.虚拟机栈和本地方法栈溢出2.1StackOverflowError2.2栈OutOfMemoryError3.方法区和运行时常量池溢出3.1运行时常量池溢出3.2方法区溢出4.本机直接内存溢出... 查看详情

深入理解java虚拟机笔记1:oom实战(代码片段)

通过代码模拟Java虚拟机规范中描述的各个运行时区域内存溢出的场景。1.Java堆溢出2.虚拟机栈和本地方法栈溢出2.1StackOverflowError2.2栈OutOfMemoryError3.方法区和运行时常量池溢出3.1运行时常量池溢出3.2方法区溢出4.本机直接内存溢出... 查看详情

深入理解jvm虚拟机读书笔记——对象的创建与内存布局(代码片段)

注:本文参考自周志明老师的著作《深入理解Java虚拟机(第3版)》,相关电子书可以关注WX公众号,回复001获取。1.对象的创建过程在Java语言层面,创建对象一般是借助new关键字去实现:Useruser=newUs... 查看详情

深入理解jvm虚拟机读书笔记——对象的创建与内存布局(代码片段)

注:本文参考自周志明老师的著作《深入理解Java虚拟机(第3版)》1.对象的创建过程在Java语言层面,创建对象一般是借助new关键字去实现:Useruser=newUser();而在虚拟机中,对象的创建过程如下:当Jav... 查看详情

深入理解jvm读书笔记二:垃圾收集器与内存分配策略

3.2对象已死吗?3.2.1引用计数法给对象添加一个引用计数器,每当有一个地方引用它的地方,计数器值+1;当引用失效,计数器值就减1;任何时候计数器为0,对象就不可能再被引用了。它很难解决对象之间相互循环引用的问题。3.... 查看详情

《深入理解jvm》读书笔记

目前只是整理了书的前几章,把jvm的内存划分简要说明、垃圾回收算法、垃圾回收器、常用的命令和工具进行说明。命令和工具的使用找个时间需要详细按步骤截图说明。还有一部分内容是举例说明了一下字节码指令的样子,可... 查看详情

深入理解jvm虚拟机:jvm运行时数据区

概述:JVM将内存的管理进行封装,使得开发人员不必关心内存申请、释放操作。但是在高级程序开发、复杂业务场景开发的时候,如果出现内存溢出的情况,对于开发人员而言就很难去分析出原因。所以还是很有必要去了解一下... 查看详情

深入理解java虚拟机(jvm性能调优+内存模型+虚拟机原理)

课程大纲:第1节说在前面的话  00:05:07分钟第2节整个部分要讲的内容说明  00:06:58分钟第3节环境搭建以及jdk,jre,jvm的关系  00:20:48分钟第4节jvm初体验-内存溢出问题的分析与解决  00:17:59分钟第5节jvm再... 查看详情