JVM
一、JVM的体系结构
-
类加载器ClassLoader:负责加载class文件,不判断它是否能运行,由Execution Engine决定。
-
Execution Engine执行引擎:负责解释命令,提交操作系统执行。
new Thread().start(); 执行该方法,有没有启动该线程?
回答:不确定,因为这个方法的底层实现是交给操作系统启动新线程,不确定操作系统有没有启动成功。
-
本地方法栈(Native Method Stack)
用于登记被native标记的java方法。
- 本地方法接口(Native Interface)
Java融合C/C++过程中,在内存中专门开辟一块区域处理标记native的代码,当被native标记的java方法被加载本地方法栈(Native Method Stack)中后,Execution Engine执行引擎 加载native libraies
-
程序计数器(Program Counter Register)
-
方法区(Method Area)
被所有线程共享,所有定义的方法的信息都保存在该区域中,属于共享区间。
存放数据:静态变量+常量+类信息(构造方法/接口定义)+运行时常量池存在方法区中
没有存在数据:实例变量存放在堆内存和方法区无关。
- Java栈
队列:先进先出
栈:后进先出
- statck栈是什么?
栈也叫栈内存,主管J ava程序的运行,在线程创建时创建,线程结束也就释放,对于栈来说不存在垃圾回收机制。生命周期和线程一致,是线程私有的。
存放数据:8种基本类型的变量+对象的引用变量+实例方法都在栈内存中分配。
- 栈帧中主要保存3类数据
本地变量:输入输出参数及方法内的变量
栈操作:记录出栈,入栈的操作。
栈帧数据:包括类文件,方法等。
- 本地方法库
二、堆体系结构
Heap堆(jdk7之前)
一个JVM实例只存在一个堆内存,大小可以调节,类加载器读取了类文件之后,需要把类,方法,常变量放到堆内存中,保存所有引用类型的真实信息,方便执行器执行。
新生区
是类的诞生,成长,消亡的区域,一个类在这里产生,最后被垃圾回收。所有的类都在伊甸区被new出来。当伊甸区的内存用完,程序对伊甸区进行垃圾回收,存活对象放置幸存0区,再经过垃圾回收机制放置1区,一区满后,gc()后继续放置0区,依次重复15后,满足条件,就放入养老区。
出现异常java.lang.OutOfMemoryError:java heap space异常,说明java虚拟机的堆内存不够。
原因:java虚拟机堆内存不够,通过-xms,-xmx调整
代码中创建了大量对象,长时间不能被回收。
永久区
常驻内驱区域,存放jdk自身所携带的class interface的元数据,运行环境。此区域不会被垃圾回收到。
jdk1.7;有永久代,常量池1.7在堆。
jdk8:没有永久代,取而代之变成元空间。常量池1.8在元空间。
三、堆参数调优
gc是什么(
分代收集算法)??
- 次数上频繁收集Young区
- 次数上较少收集old区
- 基本不动perm区
4大算法
1、引用计数法
缺点:每次对象赋值时均要维护引用计数器,而计数器本身有所消耗。
较难处理循环引用 jvm不使用该算法。
2、复制算法
年轻代中使用的是minor GC,这种GC算法采用的是复制算法。
-
没有标记和清除的过程,效率高
-
没有内存碎片
-
需要双倍空间
明显缺点:
- 浪费了一半的内存。需要两倍的内存空间
- 假设100%存活,需要将所有的对象都复制一遍,浪费时间。当存活率比较低时,这种情况可以不用考虑。
3、标记清除
老年代一般是由标记清除或者是标记清理与标记整理的混合实现。
优点:只需要一块内存空间
缺点:需要扫描两次,耗时严重。
会产生内存碎片,内存空间不连续。
4、标记压缩
老年代一般是由标记清除或者是标记清理与标记整理的混合实现。
缺点:效率不高,移动元素需要时间。
5、标记清除压缩
没有最好的算法,只有最合适的算法
年轻代
年轻代的特点是区域相对老年代较小,对象存活率低。
老年代
老年代的特点是区域较大,对象存活率高