深入JAVA虚拟机2_4OutOfMemoryError异常


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。  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 223,277评论 6 521
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,487评论 3 400
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 170,114评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,367评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,352评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,919评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,328评论 3 426
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,278评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,808评论 1 321
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,882评论 3 343
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,005评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,672评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,344评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,839评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,959评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,481评论 3 379
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,031评论 2 361

推荐阅读更多精彩内容