美文网首页深入理解java虚拟机程序员
深入JAVA虚拟机2_4OutOfMemoryError异常

深入JAVA虚拟机2_4OutOfMemoryError异常

作者: JoeyTsai | 来源:发表于2018-03-06 08:48 被阅读0次

    layout: post

    title: 深入JAVA虚拟机2_4OutOfMemoryError异常

    categories: JVM JAVA

    description: 深入JAVA虚拟机2_4OutOfMemoryError异常

    keywords: JVM JAVA

    注意:KOTLIN跟JAVA运行结果可能会不同,参照样例
    https://www.jianshu.com/p/d9f90f3ee936


    2_4_1. JAVA堆溢出测试

    测试思路

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

    code2_3虚拟机参数

    //限制java堆的大小为20mb[-Xms为堆的最小值,-Xmx为
    堆的最大值]
    //XX:+HeapDumpOnOutOfMemoryError可让虚拟机在出
    现内存溢出异常时Dump出当前内存堆转储快照以便事后
    进行分析
    参数:  -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
    

    code2_3 代码

    package num2
    
    /**
     * Created by Joey_Tsai on 2018/3/5.
     */
    class HeapOOM{
        companion object {
            class OOMObject{
    
            }
        }
    }
    
    fun main(args : Array<String>){
        val list : MutableList<HeapOOM.Companion.OOMObject> = ArrayList<HeapOOM.Companion.OOMObject>();
        var i = 0;
        while (true){
            list.add(HeapOOM.Companion.OOMObject());
    
            println(++i);
        }
    }
    

    内存泄漏 与 内存溢出

    内存泄漏:编写的程序没有正确的释放内存
    内存溢出:内存不足,无法正常给程序分配内存,导致内
    存不足的原因有很多,内存泄漏只是其中的一种
    

    解决方法

    使用工具检查GC Roots查看引用链上的对象是否都有存
    活的意义,若确实没有出现泄露的情况,应该调整堆内
    存参数(-Xmx 和 Xms)的最大值和最小值
    

    p.s

    程序运行参数图
    程序运行结果图

    github链接(使用kotlin实现)
    https://github.com/joeytsai03/JVMStudy/blob/master/src/num2/code2_3.kt



                              分    割    线
    


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

    1.虚拟机栈和本地方法栈OOM测试

    测试思路

    HotSpot虚拟机中不分虚拟机栈和本地方法栈,故-Xoss
    参数(设置本地方法栈大小)存在但实际上是无效的,栈容
    量由-Xss参数设定,关于虚拟机栈和本地方法栈java虚拟
    机描述了两种异常:
    1.如果线程请求的栈深度大于虚拟机所允许的最大深度,
      将抛出StackOverflowError异常
    2.如果虚拟机在扩展栈时无法申请到足够的内存空间,则
      抛出OutOfMemoryError异常
    

    code2_4虚拟机参数

    参数 :   -Xss128k
    

    code2_4代码

    package num2
    
    /**
     * Created by Joey_Tsai on 2018/3/5.
     * VM:-Xss128k
     */
    public class JavaVMStackSOF{
        public var stackLength : Int = 1
        public fun stackLeak() : Unit{
            stackLength++;
            stackLeak()
        }
    }
    fun main(args:Array<String>){
        val javaVMStackSOF : JavaVMStackSOF = JavaVMStackSOF()
       try {
    
           javaVMStackSOF.stackLeak()
    
       }catch (e : Throwable ){
           println("stack length : ${javaVMStackSOF.stackLength}")
           throw e
       }
    }
    

    java虚拟机栈 与 java堆 与 方法区

    1.java虚拟机栈:线程私有,生命周期与线程相同,每个方
    法在执行的过程中都会创建一个栈帧(Stack Frame)用于
    存储局部变量表,操作数栈,动态链接,方法出口等信
    息。每一个方法执行完成的过程,就对应一个栈帧在虚
    拟机栈中入栈出栈的过程。
    
    2.java堆:所有线程共享的一块内存区域,用于存放对象
    实例,垃圾收集器管理的主要区域,很多时候也被称作
    "GC堆"(Garbage Collected Heap)
    
    3.方法区:所有线程共享的内存区域,它用于存储已被虚拟
    机加载的类信息,常量,静态变量,即时编译器编译后
    的代码等数据
    
    

    实验结果

    在单线程下,无论由于栈帧太大还是虚拟机栈容量太
    小,当内存无法分配时虚拟机抛出的都是
    StackOverflowError异常
    

    p.s


    程序运行参数图
    程序运行结果图

    github链接(使用kotlin实现)
    https://github.com/joeytsai03/JVMStudy/blob/master/src/num2/code2_4.kt



    2.多线程导致内存溢出异常

    测试思路

    如果测试时不限单线程,通过不断创建线程的方式倒是
    可以产生内存溢出异常,这种情况下,为每个线程的栈
    分配的内存越大,反而越容易产生内存溢出异常
    

    使用工具

    JProfiler用于查看线程使用情况
    
    Idea安装JProfiler

    code2_5虚拟机参数

    参数:     -Xss2M
    

    code2_5代码

    package num2
    
    /**
     * Created by Joey_Tsai on 2018/3/6.
     *VM: -Xss200M
     */
    class JavaVMStackOOM{
        private fun dontStop() : Unit{
            while (true){
    
            }
        }
        public fun stackLeakByThread():Unit{
            while (true){
                val thread : Thread = Thread(Runnable(){
                    @Override
                    fun run(){
                        dontStop()
                    }
                });
                thread.start()
            }
        }
    }
    
    fun main(args : Array<String>){
        val oom = JavaVMStackOOM()
        oom.stackLeakByThread()
    }
    
    

    操作系统内存分配

    譬如,在32位的windows系统中给每个线程分配的内存
    限制为2g,虚拟机提供了参数来控制java堆和方法区这
    两部分内存的最大值,剩余的内存为2GB减去Xmx(最大
    堆容量),再减去MaxPermSize(最大方法区容量),程序
    计数器消耗内存很小,可以忽略。
    

    p.s


    程序运行参数图

    github链接(使用kotlin实现)
    https://github.com/joeytsai03/JVMStudy/blob/master/src/num2/code2_5.kt



                              分    割    线
    


    2_4_3 方法区和运行时常量池溢出

    1.运行时常量池导致的内存溢出异常

    测试思路

    运行时常量池是方法区的一部分,jdk7开始逐步去除永久
    代,String.intern()是一个Native方法,在jdk1.6及之前的版
    本中,由于常量池分配在永久代中,我们可以通过
    -XX:PermSize与 -XX:MaxPermSize限制方法区大小,从
    而限制常量池容量大小
    

    code2_6虚拟机参数

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

    code2_6代码

    package num2
    
    /**
     * Created by Joey_Tsai on 2018/3/6.
     * VM:-XX:PermSize=10M -XX:MaxPermSize=10M
     */
    class RuntimeConstantPoolOOM2_6{
    
    }
    fun main(args : Array<String>){
        //使用List保持着常量池引用,避免Full GC回收常量池行为
        val list : MutableList<String>?=ArrayList<String>();
        //10MB的permSize在integer范围内足够产生OOM了
        var i : Int = 0
        while (true){
            list?.add(i++.toString().intern())
            println(i)
        }
    }
    

    实验结果

    运行时常量池溢出,在"OutOfMemoryError"后面跟着的提示信息
    是"PermGen space",说明运行时常量池属于方法区(HotSpot虚拟机中的永
    久代)的一部分,在jdk1.7中则不会得到相同结果,while将一直循环下去。
    

    2.String.intern返回引用测试

    code2_7代码

    package num2
    
    /**
     * Created by Joey_Tsai on 2018/3/6.
     */
    public class RuntimeConstantPoolOOM2_7{
        
    }
    
    fun main(args: Array<String>) {
        val str1 : String = StringBuilder("计算机").append("软件").toString()
        println(str1.intern() == str1)
    
        val str2 : String = StringBuilder("ja").append("va").toString()
        println(str2.intern() == str2)
    }
    

    JDK1.6中的intern() 与 JDK1.7中的intern()

    在JDK1.6中,intern()会把首次遇到的字符串实例复制在永久代中,返回的
    也是永久代中这个字符串实例的引用,而StringBuilder创建的字符串实例
    在java堆上,所以必然不是同一个引用,将返回false。而JDK1.7中的
    intern()实现不会再复制实例,只是在常量池中记录首次出现的引用,因此
    intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。对
    于str2返回false是因为'java'这个字符串在执行StringBuilder.toString()之前
    已经出现过,字符串常量池已经有它的引用,不符合首次出现原则,而'计
    算机软件'这个字符串则是首次出现返回true。  
    

    相关文章

      网友评论

        本文标题:深入JAVA虚拟机2_4OutOfMemoryError异常

        本文链接:https://www.haomeiwen.com/subject/covhfftx.html