其他分享
首页 > 其他分享> > 【JVM】详解HotSpot、堆与OOM的原理并实现

【JVM】详解HotSpot、堆与OOM的原理并实现

作者:互联网

目录

参考文章

三种JVM

1.Sun        HotSpot        

2.BEA        JRockit

3.IBM        J9 VM

堆(Heap)

堆内存的划分

永久代的演变

方法区与永久代/元空间

Minor GC和Full GC的触发条件

OOM(Out Of Memory)

JVM堆常用配置参数

JVM堆初始内存大小


参考文章

 JVM系列-05-方法区-永久带VS元空间_hylexus的博客-CSDN博客_jvm方法区和元空间

Java堆内存又溢出了!教你一招必杀技【附源码】_李振良_阿良_51CTO博客

Java GC种类和触发时机_u011833033的博客-CSDN博客_gc触发时机

JVM(六)JVM优化之常用参数_jwang的博客-CSDN博客_jvm常用调优参数

三种JVM

1.Sun        HotSpot        

        我们学习和是用的都是Sun公司旗下的HotSpot虚拟机,可以通过cmd查看java版本看到

java -version

 

Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)

2.BEA        JRockit

3.IBM        J9 VM

我们学习的都是:HotSpot

堆(Heap)

在JVM中,java虚拟机栈和本地方法栈都是线程级别的,既每个线程都有对应的本地方法栈和java虚拟机栈

但是堆和方法区,在JVM只有一个,而且堆内存的大小是可以调节的。

可以在IDEA运行设置中配置虚拟机参数和配置程序参数

堆内存的划分

堆内存中被细分为三个区域,分别为

        1.年轻代(Young Generation),年轻代又可划分,其中生成区和幸存区的容量大小为8:1:1

                1.生成区(伊甸园区)(Eden)

                2.幸存区(Survivor),幸存区又可划分为

                        1.幸存0区(FromSpace)

                        2.幸存1区(ToSpace)

        2.老年代(Old Generation)

        3.永久代/元空间(Permanent Generation/MetaSpace

这里永久代和元空间并不能完全划等号

永久代的演变

        jdk1.6之前:是永久代,常量池是在方法区

        jdk1.7        :  是永久代,但是慢慢退化了,常量池在非堆中

        jdk1.8之后:没有永久代,代替的是元空间,常量池在非堆中

        元空间和永久代最大的区别在于:元空间不在虚拟机设置的内存中,而是使用了本地内存

        这一点也可以在模拟中看到

        当我们配置JVM内存时,将最大内存和初始内存设为1M,并输出GC过程

 -Xms1m -Xmx1m -XX:+PrintGCDetails

 我们可以看到年轻代合老年代在虚拟机中的总占比为1.5M

 

 这时虚拟机的内存也为1.5M,说明在jdk1.8中,元空间并不占用jvm内存

方法区与永久代/元空间

        方法区和元空间之间的关系非常模糊,方法区和永久代并不等价,但是可以牵强的解释两者等同,但是还是不同的,方法区更类似一个标准,而永久代/元空间实现了这个标准,这就相当于java中接口和接口实现一样,下文有些时候也会将两个等同

Minor GC和Full GC的触发条件

        1.当生成区(伊甸园区)(Eden)满后,会触发Minor GC,然后将不能删除的放入幸存区

至于幸存区如何进入老年代,这篇文章给出了解释

        2.该文章指出,当对象在幸存区经历了15次Minor CG后,就会被晋升到老年代,这个15也是可以配置的

        3.当老年代满后,会触发Full GC,清理老年代

        4.当老年代也无法被清理的时候,就会抛出OOM异常

 

OOM(Out Of Memory)

        根据名称就可以很清晰的看出,就是java程序在运行时内存超限了,堆中无法存放

        我们可以手动模拟一下堆的溢出(OOM),因为真正内存溢出对于普通的程序较困难实现,所以我们可以将JVM分配的内存手动减少来实现

        首先先将JVM的初始分配内存和最大分配内存都设置为2m

        然后运行如下代码

public class Main {
    public static void main(String[] args) {
        String str="123";
        while(true){
            str+=new Random().nextInt(12345678)+new Random().nextInt(999999999)+new Random().nextInt(999999999);
        }
    }
}

         代码的作用是无限向str随机拼接数据,直到程序无法继续运行,最后就会报出OOM

JVM堆常用配置参数

参考文章

1.Java堆参数

-Xms:表示初始堆大小(常用)

例如:-Xms1024m 就代表初始堆大小为1024m

-Xmx:表示最大堆大小(常用)

-Xmn:表示年轻代大小

-XX:NewRatio:设置年轻代和老年代的比值

例如:-XX:NewRatio=3,表示年轻代:老年代 = 1:3,年轻代占1/4

-XX:SurvivorRatio:设置Egen区和两个Survivor区的比值

例如:-XX:SurvivorRatio=3,表示Egen区:两个Survivor区 = 3 :2,一个Survivor区占1/5

-XX:PermSize:设置永久区初始大小,JDK1.8起无效

-XX:MaxPermSize:设置永久区的最大值,JDK1.8起无效

-XX:MetaspaceSize:初始元空间大小,JDK1.8起有效

-XX:MaxMetaspaceSize:元空间的最大值,JDK1.8起有效

2.垃圾收集器参数

-XX:+UseSerialGC:虚拟机运行在Client模式下的默认值,Serial+Serial Old

-XX:+UseParNewGC:ParNew+Serial Old,在JDK1.8被废弃

-XX:+UseConcMarkSweepGC:ParNew+CMS+Serial Old,Serial Old 收集器将作为 CMS 收集器出现 Concurrent Mode Failure 失败后的后备收集器使用

-XX:+UseParallelGC:虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS Mark Sweep)

-XX:+UseParallelOldGC:Parallel Scavenge+Parallel Old

-XX:+UseG1GC:G1+G1

3.跟踪参数

-XX:+PrintGC:获取GC的初步信息

-XX:+PrintGCDetail:打印GC的详细信息

-XX:+PrintGCTimeStamps:打印GC发生的时间戳

JVM堆初始内存大小

package com.sty;

import java.util.Random;

public class Main {
    public static void main(String[] args) {
        //jvm虚拟机试图调用的最大内存
        long maxMemory = Runtime.getRuntime().maxMemory();
        //返回jvm的总内存
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("maxMemory字节为:"+maxMemory+"     内存为:"+maxMemory/1024*1.0/1024*1.0+"M");
        System.out.println("totalMemory字节为:"+totalMemory+"     内存为:"+totalMemory/1024*1.0/1024*1.0+"M");
    }
}

通过如下代码,运行可以得到jvm虚拟机试图调用的最大内存和返回jvm的总内存

 一般来说,试图调用的最大内存为系统内存的1/4,返回jvm的总内存为系统最大内存的1/64。

标签:OOM,虚拟机,HotSpot,永久,XX,GC,内存,JVM
来源: https://blog.csdn.net/Elephant_King/article/details/122110220