JVM原理串联:jvm+GC+类加载器

本文章针对对jvm原理有一定了解的基础上,将jvm中几个关键的知识点(内存模型,GC机制,锁优化等)串联起来,组成一个完整的知识体系。
本文所有内容为笔者个人理解,或看过的知识总结,如有偏颇,望指正。

内存模型

内存模型的概念

关于内存模型的概念,以及内存模型如何工作的,这边就不做阐述,引用一篇写的比较好的文章,可以作为参考。
再有人问你Java内存模型是什么,就把这篇文章发给他。

内存模型框架

在这里插入图片描述
1、程序计数器:字符码行号指示器。
2、虚拟机栈:java方法执行的内存模型,存储局部变量,操作栈,动态栈,方法出口灯信息。使用连续的内存空间
3、java堆:保存所有对象实例,包括数组。数组也需要在堆上分配。可使用不连续的内存空间。
4、方法区:jdk1.8中,该区域已彻底移除,取而代之的是元空间。元空间不受jvm内存约束,直接在本地内存中申请内存分配。可使用不连续的内存空间。
5、运行时常量池:方法区的一部分,1.8后该区域转移到Heap堆中。

关于元空间的阐述,具体可参照下面文章
JDK8 从永久代到元空间

内存模型中对象的生命周期

对于线程独享的内存区域,生命周期与线程相同,且该区域内对象在生成时需要明确其生命周期。
对于线程共享的区域(方法区,Heap堆),生命周期与虚拟机相同。该区域内对象没有明确的死亡时间,所以GC的主要场所就在于Heap堆。

线程生命周期如下:
在这里插入图片描述

内存模型各区域大小设置及jvm启动参数

各个区域大小均在虚拟机启动时设置好,各个区域都有对应的启动参数控制其大小。主要参数如下:

  • 虚拟机栈: -Xss 设置每个线程的堆栈大小
  • java堆:-Xms 设置jvm堆最小内存;-Xmx 设置jvm堆最大内存;-Xmn 设置年轻代大小
    -Xms与-Xmx设置为相同,可以避免每次垃圾回收完成后JVM重新分配内存.
  • 方法区:-XX:PermSize:16M -XX:MaxPermSize:64m (该参数在jdk1.8中已失效)
  • 其他启动参数:
    -XX:NewRatio=4 设置年轻代与老年代的比例。年轻代:老年代 = 1:4
    -XX:SurvivorRatio=4 设置Eden区域survivor区的大小比例。S*2:E=1*2:4=2:4
    -XX:MaxTenuringThreshold=0 设置垃圾最大年龄。设置为0时,年轻代对象不经过survivor区,直接进入老年代。

GC算法及原理

传统的GC算法及原理,可参照以下博文。

GC算法及原理可参照以下博文:
深入理解 Java垃圾收集器(GC)(待补充full gc,永久代 等内容)

这里简单介绍以下jdk1.8中,元空间的GC收集算法及原理。

metaspace其实由两大部分组成

Klass
Metaspace就是用来存klass的,klass是我们熟知的class文件在jvm里的运行时数据结构,不过有点要提的是我们看到的类似A.class其实是存在heap里的,是java.lang.Class的一个对象实例。这块内存是紧接着Heap的,和我们之前的perm一样,这块内存大小可通过-XX:CompressedClassSpaceSize参数来控制,这个参数前面提到了默认是1G,但是这块内存也可以没有,假如没有开启压缩指针就不会有这块内存,这种情况下klass都会存在NoKlass
Metaspace里,另外如果我们把-Xmx设置大于32G的话,其实也是没有这块内存的,因为会这么大内存会关闭压缩指针开关。还有就是这块内存最多只会存在一块。

NoKlass
Metaspace专门来存klass相关的其他的内容,比如method,constantPool等,这块内存是由多块内存组合起来的,所以可以认为是不连续的内存块组成的。这块内存是必须的,虽然叫做NoKlass
Metaspace,但是也其实可以存klass的内容,上面已经提到了对应场景。

Klass Metaspace和NoKlass
Mestaspace都是所有classloader共享的,所以类加载器们要分配内存,但是每个类加载器都有一个SpaceManager,来管理属于这个类加载的内存小块。如果Klass
Metaspace用完了,那就会OOM了,不过一般情况下不会,NoKlass
Mestaspace是由一块块内存慢慢组合起来的,在没有达到限制条件的情况下,会不断加长这条链,让它可以持续工作。

元空间的特点

  • 充分利用了Java语言规范中的好处:类及相关的元数据的生命周期与类加载器的一致。
  • 每个加载器有专门的存储空间
  • 只进行线性分配
  • 不会单独回收某个类
  • 省掉了GC扫描及压缩的时间
  • 元空间里的对象的位置是固定的
  • 如果GC发现某个类加载器不再存活了,会把相关的空间整个回收掉

元空间的内存分配模型

  • 绝大多数的类元数据的空间都从本地内存中分配
  • 用来描述类元数据的类(klasses)也被删除了
  • 分元数据分配了多个虚拟内存空间
  • 给每个类加载器分配一个内存块的列表。块的大小取决于类加载器的类型; sun/反射/代理对应的类加载器的块会小一些
  • 归还内存块,释放内存块列表
  • 一旦元空间的数据被清空了,虚拟内存的空间会被回收掉
  • 减少碎片的策略

引用自基于JDK8的JVM内存模型详解与GC策略

类加载器与双亲委派模式

启动类加载器层级关系
在这里插入图片描述

从Java虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现(HotSpot虚拟机中),是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都有Java语言实现,独立于虚拟机外部,并且全部继承自java.lang.ClassLoader。

从开发者的角度,类加载器可以细分为:

  • 启动(Bootstrap)类加载器:负责将 Java_Home/lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
  • 标准扩展(Extension)类加载器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将Java_Home /lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
  • 应用程序(Application)类加载器:是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般称为系统(System)加载器。
  • 自定义类加载器,它们之间的层次关系被称为类加载器的双亲委派模型。该模型要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,而这种父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)。

关于这一部分内容,更详细的的,可以参照以下博文

Java类加载器与双亲委派模式的详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值