1,JVM内存结构
线程私有:
①,程序计数器
定义:当前线程所执行字节码的行号指示器
作用:选取下一条需要执行的字节码指令 (如:分支,循环,跳转,异常处理,线程恢复等依赖该计数器完成)
异常:无异常
注:
<1>,每个线程都有一个独立的程序计数器
<2>,对于Native方法,计数器值为空
<3>,虚拟机中唯一不会出现内存不足的异常OutOfMemoryError
其他:
Java虚拟机使用多线程轮流切换方式实现,同一时刻一个处理器只会执行一个线程中的指令,
为了线程切换后能恢复到正确的执行位置,每个线程都有独立的线程计数器
②,虚拟机栈
定义:Java方法执行的内存模型
作用:Java方法执行的时候,在虚拟机栈创建一个栈帧,用于存储方法相关的数据(局部变量表,操作数栈,动态链接,方法出口),
在方法执行时完成该方法在虚拟机栈中的入栈和出栈
异常:
<1>,StackOverflowError异常:
线程请求的栈深度 > 虚拟机栈允许深度
<2>,OutOfMemoryError异常:
虚拟栈动态扩展内存不足
其他:
局部变量表:存放基本类型,引用类型(非对象本身可能是引用指针,句柄等),returnAddress类型(指向字节码指令的地址)
64bit长的long和double会占用2个局部变量表空间,其他类型都是一个
③,本地方法栈
定义:Native方法执行的内存模型
作用:和虚拟机栈相同,只不过是针对Native方法
异常:
<1>,StackOverflowError异常:
线程请求的栈深度 > 本地方法栈允许深度
<2>,OutOfMemoryError异常:
本地方法栈动态扩展内存不足
其他:
本地方法栈和虚拟机栈作用相似,只是针对的方法不同;
本地方法栈和虚拟机栈可以合二为一(如:Sun HotSpot虚拟机)
线程共享:
①,堆
定义:给对象分配内存和存放对象实例的内存区域
作用:给对象分配内存,存放对象实例
异常:OutOfMemoryError(堆内存不足)
配置:
-Xmx(最大堆内存)
-Xms(最小堆内存)
-XX:NewSize(新生代最小空间)
-XX:MaxNewSize(新生代最大空间)
-Xss(每个线程的堆栈大小)
根据GC采用的分代收集算法:
<1>,新生代:Eden空间,From Survivor空间, To Survivor空间
<2>,老年代
其他:
<1>,虚拟机中占内存最大的部分
<2>,线程共享
<3>,不是所有对象都在堆内存分配内存,如:方法区给静态常量分配内存
<4>,GC的主要区域
<5>,堆内存可以处于不连续的物理内存空间上
②,方法区
定义:存放类和编译器相关信息的内存区域
作用:存储虚拟机加载的类信息,常量,静态变量,编译器编译的代码等数据
配置:
-XX:PermSize(永久代最小空间)
-XX:MaxPermSize(永久代最大空间)
垃圾回收: 常量池,对类型的卸载
异常:OutOfMemoryError(方法区内存不足)
其他:
<1>,线程共享
<2>,堆的一个逻辑区域
<3>,又名永久代,是HotSpot虚拟机为了对方法区进行垃圾回收的一种方式
<4>,垃圾回收对类型的卸载条件严格,很容易出现内存泄漏
扩展:
【1】,运行时常量池
定义:属于方法区的一部分内存区域;
作用:用于存放Class文件中常量池(存放类在编译期生成的各种字面量和符号引用(如:CONSTANT_*等))
异常:OutOfMemoryError(常量池内存不足)
其他:
<1>,根据不同的虚拟机实现也可存放直接引用
<2>,编译期和运行期都可以操作常量池(如:String.intern()方法运行期操作常量池)
2,直接内存
定义:不属于虚拟机运行时数据区,非虚拟机规范定义的内存区域,属于机器的物理内存
异常:OutOfMemoryError(直接内存不足)
其他:
JDK1.4加入的NIO类(使用了通道和缓冲区的I/O方式)使用Native函数库可以直接分配直接内存,
并通过Java堆中的DirectByteBufer对象作为这块直接内存的引用进行操作,避免了Java堆和Native堆中来回赋值数据
3,对象分配规则
1,对象优先进入Eden区域,如果新生代Eden空间不足进行一次MinorGC
2,需要大量连续内存的大对象直接进入老年代(目的:避免Eden和Survivor间大量内存拷贝)
-XX:PretenureSizeThreshold 设置可以直接进入老年代对象的大小(仅适用Serial/ParNew组合收集器和ParNew/CMS组合收集器)
3,长期存活的对象进入老年代:每次MinorGC给新生代对象的年龄计数器+1,对象年龄达到临界值直接进入老年代
-XX:MaxTenuringThreshold设置晋升老年代对象的年龄阈值
4,动态对象年龄判断:如果新生代Survivor中相同年龄对象大小总和 > Survivor大小一半; 则大于等于该年龄的对象直接进入老年代并不受MaxTenuringThreshold参数限制
5,空间分配担保:
发生Minor GC之前,检查
虚拟机检查老年代最大可用连续空间 > 新生代所有对象总空间:Minor GC可以确保安全;
虚拟机检查老年代最大可用连续空间 < 新生代所有对象总空间:判断HandlePromotionFailure(是否允许担保失败)
HandlePromotionFailure = true : 老年代最大可用连续空间 > 历次晋升到老年代对象的平均大小,进行Minor GC
老年代最大可用连续空间 < 历次晋升到老年代对象的平均大小,进行Full GC
HandlePromotionFailure = false:进行Full GC
担保是有风险的:
因为新生代采用复制收集算法,为了内存利用率,只使用其中一个Survivor空间作为轮换备份,如果Minor GC后新生代所有对象都存活的极端情况,
就需要老年代来进行分配担保,将Survivor不能容纳的对象直接进入老年代; 而在实际完成回收之前有多少对象存活下来不确定,因此老年代本身
是否有容纳这些对象的剩余空间也是不能确定的所以分配担保是有风险的;因此取之前每次晋升到老年代对象容量的平均大小最为经验值和老年代剩余
空间进行比较,决定是否Full GC以便老年代有更多的剩余空间。
Minor GC和Full GC区别:
【1】Minor GC(新生代GC):发生在新生代的垃圾收集动作;
因为Java对象大多数朝生夕灭,所以Minor GC非常频繁,一半回收速度比较快
【2】Major GC/Full GC(老年代GC):发生在老年代的GC;
出现了Major GC经常会伴随至少一次的Minor GC(不是绝对的,Parallel Scavenge收集器就直接进行Major GC);Major GC速度一半比Minor GC慢10倍以上。
JVM内存模型1.7和1.8区别:
图片来源:https://www.analysys.cn/article/detail/20019016