初探JVM

1. JVM的体系结构

image-20210301232142362

  • 其中栈区由于函数返回后就会出栈,因此不会存在垃圾回收。垃圾回收主要针对的是堆区的回收。
  • JNI:Java Native Interface,通过使用Java本地接口书写程序,可以确保代码在不同的平台上方便移植。

2. 类加载器

类的生命周期

image-20210301232732796

类的加载过程详细参考Class文件是如何被加载进JVM

类加载器的分类

BootstrapClassLoader(启动类加载器)

c++编写,加载java核心库 java.*,构造ExtClassLoaderAppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作

ExtClassLoader (标准扩展类加载器)

java编写,加载扩展库,如classpath中的jrejavax.*或者
java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。

AppClassLoader(系统类加载器)

java编写,加载程序所在的目录,如user.dir所在的位置的class

CustomClassLoader(用户自定义类加载器)

java编写,用户自定义的类加载器,可加载指定路径的class文件

双亲委派机制

特点

  • 一个类加载器收到了类加载请求,不会自己立刻尝试加载类,而是把请求委托给父加载器去完成,每一层都是如此,所有的加载请求最终都传递到最顶层的类加载器进行处理;
  • 如果父加载器不存在了,那么尝试判断有没有被启动类加载器加载;
  • 如果的确没有被加载,则再自己尝试加载。

流程

image-20210301233553475

好处

  1. 双亲委派机制使得类加载出现层级,父类加载器加载过的类,子类加载器不会重复加载,可以防止类重复加载
  2. 使得类的加载出现优先级,防止了核心API被篡改,提升了安全,所以越基础的类就会越上层进行加载,反而一般自己的写的类,就会在应用程序加载器(Application)直接加载。

3. 数据区是如何工作的

参考文章Java运行时数据区域是如何工作的

4. 堆

Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节的。一般堆中存放的是类,方法,常量,变量,以及所有引用类型的真实对象

堆区逻辑结构

堆区的逻辑结构分为两部分,非堆区和堆区,非堆区即方法区,JDK8之前叫做永久带,JDK8之后叫做元空间。堆区主要分为老年带和新生带。结构图如下:

image-20210301235528368

如上图所示,堆区的主要结构可细分为:

  • 新生区
    • Eden区
    • 幸存from区
    • 新区to区
  • 老年区

新生区

Young GC

新生区是类诞生和成长的地方,甚至会在这里死亡。当一个对象被new出来时,会首先进入Eden区,Eden区慢慢积累满了之后,触发第一次Young GC,存活对象拷贝到Survivor的from区,清空Eden区。当第二次Eden区满时,再次触发Young GC,扫描Eden区和from区,把存活的对象复制到To区,清空Eden区和from区。如果此时Survivor区的空间不够了,就会提前把对象放入老年代。

Full GC

当新生带都满了时,即Young GC已经清理不出空间时,就会触发Full GC,这次重GC会将整个堆中的对象做一次GC。如果还是没有空闲的空间腾出,就会抛出异常:java.lang.OutOfMemoryError: Java heap space

老年区

由新生区过渡而来,默认的,新生区中一个对象在from区和to区来回交换15次后,如果对象最终还是存活,就放入老年代。

永久带/元空间

这个区域是常驻内存的。用来存放JDK自身携带的Class对象,Interface元数据,存储的是Java运行时的一些环境或类信息,这个区域不存在垃圾回收!当关闭JVM虚拟机就会释放这个区域的内存。

元空间:逻辑上存在,物理上不存在

5. GC垃圾回收

参考Java垃圾回收机制

判断对象可用算法

引用计数法

给对象一个引用计数器refCount。每有一个对象引用它,计数器加1,当refCount=0的时候,表示对象不再可用。

缺点:对象循环引用时,即使两个对象都不再被访问,计数器也不为0.

可达性分析算法

image-20210302001828753

如上图,从GC Roots开始向下搜索,连接的路径为引用链;GC Roots不可达的对象被判为不可用。

垃圾回收算法

复制算法

image-20210302002206380

  • 将可用内存分为容量大小相等的两块,每次只使用其中一块;
  • 当一块用完,就将存活着的对象复制到另一块,然后将这块全部内存清理掉;

一般新生代(伊甸园区、幸存区)会使用复制算法,生成新的to区

优点:没有内存碎片。

缺点:内存的利用率变低,可用内存缩小为原来的一半;如果存活数量比较大,复制性能会变低。

标记清除

image-20210302002537612

缺点:两次扫描,严重浪费时间,会产生内存碎片。

优点:不需要额外的空间。

标记压缩

对于标记清除的再压缩

image-20210302002659660

缺点:整理阶段存在效率问题,适合老年代这种垃圾回收频率不是很高的场景。

优点:不会产生内存碎片;不需要浪费额外的空间进行分配担保。

分带收集算法

当前商业虚拟机都采用该算法。

  • 新生代:复制算法(GC后只有少量的对象存活)
  • 老年代:标记-清除-压缩算法 (GC后对象存活率高)
posted @ 2021-03-02 00:35  bGpi  阅读(46)  评论(0编辑  收藏  举报