【JVM】详解HotSpot、堆与OOM的原理并实现
作者:互联网
目录
参考文章
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