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实例的强引用。

  1. 强引用可以直接访问目标对象

2.强引用指向的对象在任何时候都不会被系统回收(只能被GC清除),即使OOM

3.强引用可能导致内存泄漏

软引用

软引用是比强引用弱一点的引用,当堆空间不足时,就会被回收。

每一个软引用可以附带一个引用队列,当对象的可达性发生改变时,软引用进入队列,通过引用队列,可以跟踪对象的回收情况。

弱引用

在GC时发现就回收,也会加入引用队列

虚引用

随时都可能被回收,必须和引用队列一起使用,用于跟踪垃圾回收过程

STW停顿

为了让垃圾回收器可以正常高效的执行,大部分情况下会要求系统进入一个停顿状态Stop The World.只有这样才能拨正系统状态在某一个瞬间的一致性。

垃圾回收器

目前JDK使用的垃圾回收期:G1

以上是 JVM笔记 的全部内容, 来源链接: utcz.com/z/512294.html

回到顶部