jvm

2023-07-17  本文已影响0人  追风还是少年

jvm主要组成部分

包含两个子系统(类加载器、执行引擎)和两个组件(运行时数据区、本地库接口)


jvm组成
  • 运行时数据区域:即我们常说的jvm内存
  • 执行引擎:执行class中指令
  • 本地库接口:与本地方法库交互,与其它编程语言交互的接口
  • 类加载器:装载class文件到运行时数据区的方法区
代码执行过程

编译器把java代码编译成字节码,类加载器再把字节码加载到内存,放入运行时数据区的方法区,命令解析器执行引擎再将字节码翻译成底层系统指令,再交由CPU去执行,过程中可能需要调用其它语言的本地库接口来实现整个功能。

jvm内存模型(jvm运行时数据区)

运行时数据区
  • 方法区:存储类信息、常量、静态变量、即时编译后代码
  • java堆:几乎所有对象实例分配在java堆
  • 虚拟机栈:服务于java方法,存储局部变量表、操作数栈、动态链接、方法出口
  • 本地方法栈:与虚拟机栈一样,只不过是服务于调用native方法
  • 程序计数器:当前线程执行的字节码的行号指示器,通过改变计数器的值来选取下一条需要执行的字节码指令

对象分配内存

内存分配的两种方式:

  • 指针碰撞,已使用内存放一边,空闲内存放另外一边,已经使用内存和空闲内存中间有一个指针作为分界点的指示器,分配内存时,只需要把指针往空闲内存那边移动一段与对象大小相等的距离
  • 空闲列表,已使用内存与空闲内存相互交错,虚拟机必须维护一个列表来记录哪些内存空间是空闲的,分配内存时,从列表找一块足够大的空间划分给对象,并更新列表上的记录

对象的创建是非常频繁的,哪怕只是修改一个指针指向的位置,在并发情况下也是不安全的,可能出现正在给A对象分配内存,指针还没来得及修改,对象B又同时使用该指针来分配内存的情况。

内存分配的并发安全处理:

  • 同步处理,CAS + 失败重试来保证操作的原子性
  • 本地线程分配缓存(Thread Local Allocation Buffer,TLAB),每个线程在java堆中预分配一小块内存,称为本地线程分配缓存,每个线程分配内存时,只要在自己的TLAB上分配内存;只有线程的TLAB用完并需要分配新的TLAB时,才需要同步锁。-XX:+/-UserTLAB参数设置是否使用TLAB

对象访问定位

对象访问一般是通过栈上的引用访问堆中的具体对象,对象访问方式取决于JVM虚拟机的实现,目前主流的访问方式有句柄和直接指针两种。

直接指针:指向对象,代表一个对象在内存中的起始地址
句柄:可以理解为指向指针的指针,维护着对象的指针。句柄不直接指向对象,而是指向对象的指针(句柄不发生变化,指向固定内存地址),再由对象的指针指向对象的真实内存地址

  • 句柄访问:Java堆中划分一块内存作为句柄池,引用中存储对象的句柄地址,而句柄中包含对象实例数据的具体地址信息和对象类型数据的具体地址信息。
    优点:引用中存储的是稳定的句柄地址,在对象被移动时,只会改变句柄中的实例数据指针,而引用本身不需要修改
  • 直接指针:引用中存储的直接是对象地址,那么java堆对象内部的布局中就必须考虑如何放置访问数据类型的相关数据
    优点:速度更快,节省了一次指针定位的时间开销,由于对象的访问在java中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。Hotspot中就是采用这种方式


    句柄
直接指针

对象引用类型

java中有哪些引用类型:

  • 强引用,即使内存空间不足,也不会回收
  • 软引用,内存空间足够,不会被回收,内存空间不足,就会回收
  • 弱引用,不管内存是否足够,只要gc就回收
  • 虚引用,任何时候都会被垃圾回收器回收,主要用于跟踪垃圾回收器回收的活动

堆内存分代

  • 新生代,存放新生对象,使用复制算法
  • 老年代,存放生命周期长的对象,使用标记清除算法
  • 堆 = 新生代(Young) + 老年代(Old)
  • 新生代 = Eden + S0(from) + S1(to)

默认配置:
新生代:老年代 = 1:2,通过-XX:NewRatio配置
Eden:From:To = 8:1:1,通过-XX:NewSurvivorRatio配置

判断对象是否可回收

分代收集

现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。
一般将堆分为新生代和老年代。

垃圾收集器
image.png

以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。

CMS收集器
CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。
分为以下四个流程:

在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿

G1 收集器
G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收

image.png

G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离


image.png

通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。
这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。通过记录每个 Region 垃圾回收时间以及回收所获得的空间(这两个值是通过过去回收的经验获得),并维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。
每个 Region 都有一个 Remembered Set,用来记录该 Region 对象的引用对象所在的 Region。通过使用 Remembered Set,在做可达性分析的时候就可以避免全堆扫描。

Minor GC、Major GC、Full GC

内存分配策略

jvm命令

#显示Java堆详细信息,包括使用的GC算法、堆配置信息和各内存区域内存使用信息
jmap -heap pid
#显示堆中对象的统计信息,包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名
jmap -histo:live pid
jstat -gcutil 2815 1000 
上一篇下一篇

猜你喜欢

热点阅读