JVM笔记
JVM架构
类加载系统负责从网络或者系统中加载Class信息,加载的类 信息及常量信息(类及其方法实现)存放于元数据区的内存空间【1.8开始方法区已经被移到元数据区】
堆是主要的内存工作区域,对象实例存放于堆中,并且是线程共享的。
直接内存NIO允许程序直接使用堆外的系统内存,内存速度优于Java堆,因此频繁读写的场合可以考虑使用直接内存
垃圾回收系统可以针对==方法区== ==堆== ==直接内存== 进行回收,其中 堆 是工作回收的重点工作区域
栈每一个线程都有一个栈,栈在线程创建的时候被创建,栈中保存着一个个==帧栈== ,帧栈中保存着 ==局部变量== ==方法参数== ==方法调用== ==返回==。
本地方法栈和栈很类似,Java栈用于Java的方法调用,而本地栈用于本地方法的调用,使得Java直接调用本地的方法。
PC寄存器也是一个线程一个PC寄存器,++一个线程总是在执行一个方法++,当前方法如果不是本地方法,PC寄存器就会指向当前正在被执行的指令。如果是本地方法那么PC寄存器的值为undifined
执行引擎负责执行虚拟机的字节码。
堆
堆和程序数据密切相关
根据垃圾回收机制的不同,堆可能拥有不同的结构。
最常见JVM Hotspot如下:
分为新生代和老年代,新生代也分为Eden(伊甸园)和两个Survivor(存活区)。Survivor分别是S0 和S1。
1. 在一般情况下对象首先分配在伊甸园,在一次新生代垃圾回收后Young GC出发清除策略,
GCRoot标记没有被引用的对象被回收,如果对象还活着,那么就会进入存活区里 S0 或者S1,
具体送到哪个空间?--YGC时将活下来的对象复制到未使用的那块存活区,然后将正在使用的存活区全部清除,同时交换两个空间的使用状态。
之后每一次新生代回收,对象如果活了下来年龄就会+1,当年龄到达一定程度后,就会被任务是老年对象,从而进入老年代。
2. 如果YGC要移送的对象大于了Survivor区的容量上限或者Survivor放不下,则直接移交给年老代。
正常在新生代生活的默认年龄是15,也就是在存活区生活到14岁后。
JDK8 开始 字符串常量存放在堆内存,其他内容包括类的元信息,静态属性,整型常量等都移动到元空间内。
栈
栈和线程执行密切相关
每一次函数调用都会有一个对应的帧栈被压入该线程的栈,每一个函数调用结束都有一个帧栈被弹出。它保存着函数的局部变量,中间运算结果等数据。
不论是return指令还是抛出异常都会让帧栈弹出
在一个帧栈中至少要包含:局部变量表,操作数栈,帧数据区 几个部分
如果栈空间不足,函数就会抛出StackOverflowError 栈溢出错误
局部变量表
保存函数的参数和局部变量,数据参数过多或者局部变量过多,区域消耗过多的空间,会减少栈的嵌套调用次数。
操作数栈
用于保存计算过程的中间结果,同时做计算过程中变量临时的存储空间
帧数据区
用来支撑常量池解析,正常的方法返回和异常处理等
栈上分配
默认开启(标量替换)
栈上分配是java虚拟机提供的一项优化技术,它对于那些线程私有的对象(不可能被其他线程访问的对象),将他们打散分配在栈上,而不是堆上,这样的好处是函数调用后可以自行销毁,不需要GC的介入。
元数据区
一块堆外的直接内存,不指定大小的情况下,会消耗系统的所有可用内存。
垃圾回收概念与算法
常见的垃圾回收算法
引用计数法
标记清除法
复制算法
标记压缩算法
分代算法
分区算法
什么才是真正的垃圾
垃圾回收的基本思想是考察对象根节点可达性,如果根节点都不可达说明对象不再使用了。
但是,他现在还不是真正的垃圾,因为他有可能会被复活。
只有不可触及的对象才是真正的垃圾
可触及的
从根节点开始,可以到达这个对象
可复活的
对象的引用都被释放,但是有可能对现在finalize()函数中被复活
不可触及的
对象的finalize()函数被调用,并且没有复活,那么才会 进入不可触及状态,不可触及的对象 不可能被复活,因为finalize()函数只会被调用一次。
对象的复活
用一段代码来说明
public class GC { public static GC SAVE_HOOK = null;
public static void main(String[] args) throws InterruptedException {
SAVE_HOOK = new GC();
SAVE_HOOK = null;
System.gc();
Thread.sleep(500);
if (null != SAVE_HOOK) { //此时对象应该处于(reachable, finalized)状态
System.out.println(”Yes , I am still alive”);
} else {
System.out.println(”No , I am dead”);
}
SAVE_HOOK = null;
System.gc();
Thread.sleep(500);
if (null != SAVE_HOOK) {
System.out.println(”Yes , I am still alive”);
} else {
System.out.println(”No , I am dead”);
}
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println(”execute method finalize()”);
SAVE_HOOK = this;
}
}
结果
第一次GC
Yes , I am still alive
第2次GC
No , I am dead
不建议用finalize方法完成清理工作
引用和可触及性的强度
强引用
强引用对象是可触及的,“不会被回收的”。
比如:
Object obj=new Object();
如果上述代码运行在方法中,那么局部变量obj将被分配到栈上,而对象Object实例可能会被分配到堆上,通过obj操作Object那么obj就是Object实例的强引用。
- 强引用可以直接访问目标对象
2.强引用指向的对象在任何时候都不会被系统回收(只能被GC清除),即使OOM
3.强引用可能导致内存泄漏
软引用
软引用是比强引用弱一点的引用,当堆空间不足时,就会被回收。
每一个软引用可以附带一个引用队列,当对象的可达性发生改变时,软引用进入队列,通过引用队列,可以跟踪对象的回收情况。
弱引用
在GC时发现就回收,也会加入引用队列
虚引用
随时都可能被回收,必须和引用队列一起使用,用于跟踪垃圾回收过程
STW停顿
为了让垃圾回收器可以正常高效的执行,大部分情况下会要求系统进入一个停顿状态Stop The World.只有这样才能拨正系统状态在某一个瞬间的一致性。
垃圾回收器
目前JDK使用的垃圾回收期:G1
以上是 JVM笔记 的全部内容, 来源链接: utcz.com/z/512294.html