深入理解JVM
1. JVM运行机制及原理
JVM是JRE的一部分,是一个虚构出来的计算机,有着一套完整的硬件架构体系。
java程序之所以拥有着跨平台性,这也就是jvm的功劳。首先java源程序.java文件通过编译器编译成.class文件,然后java解释器又将这编译后的字节码,解释成特定的机器码运行。
2. jvm内存机制
Jvm内存区域分为三大块:类装载器、运行时数据区、执行引擎。
2.1 类装载器
类装载器负责加载程序中的类型(类和接口),并赋予唯一的名字。同时类装载器分为启动类装载器和用户自定义类装载器。
2.2 运行时数据区
- 运行时数据区由以下几个部分组成:
- 堆内存:
存放程序运行时new出来的对象,同时堆内存在JVM中属于线程共享的,因此对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的。开发中也常使用单例模式或者配置一个bean容器,来创建一个常用的实例对象。 - 栈内存:
每个方法被执行的时候,都会同时创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、对象引用地址、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 - 程序计数器:
一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器。 - 本地方法栈:
与虚拟机栈作用很相似,区别是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是为虚拟机用到的Native方法服务。(Java程序调用了非Java代码) - 方法区:
用于存放已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。
方法区同时包含运行时常量池,用于存放编译期生成的各种字面量和符号引用。这里也有一个特性,就是对象比较。(注意:jdk1.8常量池位于方法区中,但常量池引用的字符串实例却在堆中。)
2.3 执行引擎
用来执行字节码或本地方法。主要采用方式:解释,即时编译,自适应优化。
3. JVM垃圾回收
- GC什么时候启动
GC是一个动态优先级的守护进程。当内存资源低到一定限度时便会自动运行,从而实现对内存的回收。这就是垃圾回收的时间不确定的原因。
告诉程序尽快启动一次GC代码:system.gc()
- 被GC回收的对象
程序运行期间,所有对象实例存储在运行时数据区域的heap中,当一个对象不再被引用(使用),它就需要被收回。在GC过程中,这些不再被使用的对象从heap中收回,这样就会有空间被循环利用。 - GC是如何回收对象的
在对对象的生命周期特征(eden or survivor)进行分析之后,采用了分代的方式进行对象的收集,以缩短GC对应用造成的暂停。
这种"自适应式"垃圾回收机制,会自动的进行分代、停止-复制、标记-清扫等方式进行垃圾回收。它会根据不同的环境选择不同的处理方式。 - JVM次世代划分
新生代与旧生代策略作用于堆内存中,而持久代策略作用于方法区中- 新生代 Young Generation
1. Eden Space 任何新进入运行时数据区域的实例都会存放在此
2. S0 Suvivor Space 存在时间较长,经过垃圾回收没有被清除的实例,就从Eden 搬到了S0
3. S1 Survivor Space 同理,存在时间更长的实例,就从S0 搬到了S1 - 旧生代 Old Generation/tenured
同理,存在时间更长的实例,对象多次回收没被清除,就从S1 搬到了tenured - 持久代 Perm
存放运行时数据区的方法区
- 新生代 Young Generation
- 开发中可回收垃圾产生演示
JVM 分别对新生代和旧生代采用不同的垃圾回收机制
何为垃圾?
Java中那些不可达的对象就会变成垃圾。那么什么叫做不可达?其实就是没有办法再引用到该对象了。主要有以下情况使对象变为垃圾:
- 对非线程的对象来说,所有的活动线程都不能访问该对象,那么该对象就会变为垃圾。
- 对线程对象来说,满足上面的条件,且线程未启动或者已停止。
例如:
(1)改变对象的引用,如置为null或者指向其他对象。
Object x=new Object();//object1
Object y=new Object();//object2
x=y;//object1 变为垃圾
x=y=null;//object2 变为垃圾
(2)超出作用域
if(i==0){
Object x=new Object();//object1
}//括号结束后object1将无法被引用,变为垃圾
(3)类嵌套导致未完全释放
class A{
A a;
}
A x= new A();//分配一个空间
x.a= new A();//又分配了一个空间
x=null;//将会产生两个垃圾
(4)线程中的垃圾
class A implements Runnable{
void run(){
//....
}
}
//main
A x=new A();//object1
x.start();
x=null;//等线程执行完后object1才被认定为垃圾
这样看,确实在代码执行过程中会产生很多垃圾,不过不用担心,java可以有效地处理他们。