简单理解JVM机理(水满则溢)

理解Java虚拟机须从四个方面入手:

  1. JVM是如何进行内存分配的
  2. 如何保证正确的回收
  3. JVM什么情况下触发GC以及GC的过程
  4. 如何监控优化GC

首先,从JVM内存分布图入手:
在这里插入图片描述
图片引用链接https://blog.csdn.net/sinat_30293905/article/details/82463204

  1. 程序计数器(该内存区域是JVM中唯一没有规定OutOfMemoryError的区域),
    一块为线程所私有的较小内存空间,由多个计数器组成,用于记录当前线程执行字节码行数的。
    每个线程都需要记录执行的行数,因此每个计数器只记录一个线程的执行行数。
    (Java的多线程是通过线程轮换分配时间片来实现的,因此在任一时刻,同一CPU只执行单个线程指令,当线程进行切换时,为了线程能恢复到正确的位置,每个线程必须有独立的计数器,保证线程间互不影响)
    注意:
    若线程执行的时一个Java方法,计数器记录的是虚拟机字节码指令地址;
    若执行的是Native方法,计数器指令为空

  2. 虚拟机栈,一个为线程所私有的,生命周期与线程同步的栈空间(每个线程都有一个属于自己的虚拟机栈)。每个线程方法在执行时,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出入口等信息,每个方法的调用到执行的过程就是一个栈帧入站到出栈的过程
    虚拟机栈规定了两种异常情况:
    不允许进行动态扩容情况下,线程请求栈深度大于虚拟机栈所允许的深度,会抛StackOverFlowError异常
    允许动态扩容情况下,请求扩容的内存大小无法分配到,会抛OutOfMemoryError异常

  3. 本地方法栈,与虚拟机栈基本相同,唯一的区别在于虚拟机栈执行的是Java方法,本地方法栈执行的是Native方法

  4. Java堆(线程共享的内存区域),堆区是JVM管理的内存中最大的一块,主要存储New出来的对象实例

  5. 方法区(又称永久区,线程共享的内存区域),存储被虚拟机加载的类信息、常量、静态变量、即时编译代码数据等
    方法区的GC是比较少的,这也是方法区被称为永久区的原因,但方法区也可以执行GC,该区域主要针对常量池和类型的卸载。另外,方法区也会抛OutOfMemoryError异常(比如,方法区中的常量池溢出的时候会抛出该异常)

  6. 直接内存,不是JVM运行时的一部分,可以直接访问堆外内存;所以当内存空间无法承载的时候,也会出现内存溢出的问题

以上基本就是JVM的内存分布情况,简单理解 “ 水满则溢 ”

理解了内存分布后,再来看内存是如何进行分配的:
以 Student stu = new Student() 为例:Student stu 作为引用对象存在虚拟机栈上;new Student() 存在Java堆中,堆中记录Student类型的信息,包括方法、接口、对象类型等地址;这些类型的执行数据(常量、静态变量等)存在方法区当中

对象访问的形式分两种:句柄访问&&直接指针访问

  1. 句柄访问:Java堆中划分一块句柄池,包括对象的实例数据和对象类型的数据地址(指向方法区)。在虚拟栈中存放句柄池地址,基本分布如下:
    在这里插入图片描述
  2. 直接指针访问:虚拟机栈直接指向Java堆中的对象类型指着对象的实例数据,然后对象类型指针再指向方法区中对象类型的实例数据,分布如下:
    在这里插入图片描述
    HotSpot属于第二种访问方式,优点在于访问速度快,省去一次指针开销时间。

如何确保正确回收?
回收是回收已经没有用的对象。如何判断一个对象没有被引用?
有两种方法:引用计数法&&可达性分析算法
3. 引用计数法:对象中添加一个引用计数器,每当有一个地方引用,计数器++,引用失效,计数器- -,计数器为0即为不可用
(缺点:无法处理对象直接相互引用的问题,因为相互引用后无法使计数器为0,导致无法回收)
4. 可达性分析算法(GC Root):当一个对象没有与任何引用链相连时,对该对象进行回收

在这里插入图片描述

扩充Java对引用的概念:

  1. 强引用:垃圾回收器永不回收的引用对象
  2. 软引用:在系统资源匮乏时,会把这些对象列入回收范围进行回收
  3. 弱引用:弱引用对象只能生存到下次垃圾回收前,垃圾回收时,无论内存是否匮乏,都会回收
  4. 虚引用:完全不会对对象构成影响,也无法通过虚引用取得对象实例。存在的目的就是为了一个虚引用对象被回收时收到一个系统通知

JVM什么情况下触发GC以及GC触发的方式:
触发GC情况:内存空间不足时,采用分代收集的算法(年轻代和老年代)
年轻代:当一个对象被创建时,内存分配首先分配在年轻代,大部分对象创建以后都不再使用,对象很快变得不可达(可达性分析算法),即无用,因此被清除掉,被称为Young GC
老年代:对象若在年轻代存活了足够长的时间且未被清除掉,则会被复制到老年代,老年代的空间一般比年轻代大,能存放更多的对象,在老年代上发生的GC称为Full GC

对象如何被kill的流程:

  1. 当一个对象被 new 出来时,先在年轻代 Eden 区被创建,直到 GC 的时候,根据可达性算法,看一个对象是否消亡,未消亡则进入年轻代 Survivor 区,消亡直接 Kill掉
  2. 进入 Survivor 区的对象,当下一次 GC 的时候,还是会被检查是否存活,若存活则被放入另一块 Survivor 区中(在年轻代中有两个Survivor 和一个 Eden)
  3. 当一个对象在两个 Survivor 区中切换多次后,会进入老年代,当老年代内存空间不足的时候,会触发Full GC,对消亡的对象Kill

GC的算法:标记–清除,复制,标记–整理(以后有空再详叙)
参考博文:https://www.cnblogs.com/wtzbk/p/7985156.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值