了解JVM的内存管理机制

一、前言

在前面的章节了解了class的文件结构和类加载机制,所有的这些数据都是运行在内存中,因此接下来需要了解JVM的内存管理机制。

二、运行时内存区域划分

JVM在运行的时候,把自己所管理的内存分为若干区域,每个区域有各自的功能。从线程的角度出发,分为共享内存区域私有内存区域

  1. 线程私有内存区域,包括以下三块
  • 程序计数器
  • java虚拟机栈
  • 本地方法栈
  1. 共享内存区域
  • 堆:对象实例(GC新生代、老年代)
  • 方法区(类信息、常量、静态变量、及时编译后的代码等)
  • 运行时常量池(字面量、符号引用)

image

三、对象探秘

程序在JVM运行中,会需要很多的对象,那么我们需要了解对象是如何创建、布局以及访问的。

3.1 对象的创建

当遇到new关键字时,从常量池中定位到对应类的符号引用,并检查该符号引用所代表的类是否被加载、解析和初始化。如果没有则执行相关类的初始化过程。类加载检查通过,为对象分配内存,对对象进行必要设置,对象头:对象哈希吗、GC分代信息、哪个类实例.最后执行方法,对相关的字段进行赋值。

3.2 对象布局

对象头,实例数据、对其填充

3.3 对象访问

创建了一个对象目的就是为了使用,java程序通过栈上的refrence的数据来操作对上的具体对象。目前使用两种方式来访问对象的具体位置:

3.3.1 句柄

在堆中划分一块内存来作为句柄池:refrence中存储的就是对象的句柄地址,句柄中包含了对象的实例数据和类型数据各自的具体地址信息。

image

3.3.2 直接指针

对象实例中需要存储类型数据的指针。

image

四、GC和内存分配策略

在了解完对象的创建,访问以及在对象JVM的内存分配情况后,我还需要了解对象的回收机制。

4.1 对象已死吗?

一个对象是否可以被回收或者已经死掉,可以有以下两种判断方法:

4.1.1 引用计数算法

使用加1,没有使用减1,为0代表可以回收,但存在相互引用问题,导致无法判断

4.1.2 可达性分析算法

主流实现方式,通过GC Roots的对象作为起点,从这些节点开始向下搜索,搜索所走过的引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,可判断为可以回收的对象。

可以GC Root对象的包括以下几种:

  1. 虚拟机栈(栈帧中本地变了表)所引用的对象
  2. 本地方法栈中所引用的对象
  3. 方法区的静态属性引用的对象
  4. 方法去中常量引用的对象

4.2 再谈引用

引用分为以下几种类型:

强引用(Strong refrence)

软引用(SoftWeakRefrece)内存溢出之前进行回收

弱引用(WeakRefrence) 下一次GC之前进行回收

虚引用 回收的到一个通知

这里的引用的软引用和弱引用在实际开发中用得比较多,比如一些缓存框架,则使用了软引用,用空间换时间的方式提升整体的运行体验;弱引用则在一些生命周期较长的对象中使用,比如Handler,弱引用Activity,从而防止因Handler生命周期过长导致Activity的内存泄漏。

4.3 GC算法

下面介绍几种常用的收集算法以及优缺点以及演变过程,最后说一下现代GC的算法。

  1. 标记清除算法:效率不高、大量碎片化
  2. 复制算法:将内存1:1分配,回收的时候将存活的对象复制到另外一半,然后对另外一半整体回收。
  3. 标记整理算法:标记后将可用对象向一边移动,然后回收临界点后面的区域
  4. 分代收集算法:将内存分为新生代和老年代:新生代的对象大多为朝生夕死,98%概率,因此适用复制算法,这里将内存一个Eden(80%)+两个Survivor(10)空间,回收时将Eden空间和一个Survivor空间可用的复制到另外一个Survivor空间,然后对Eden空间和对应的一个Survivor进行回收,如果其中一个Survivor空间不够,则由老年代进行担保,直接进入老年代。因为老年代的对象存活率高,没有额外的空间对他进行分配担保,因此必须要使用标记清楚或者标记整理算法对其进行回收。

参考文献

深入理解Java虚拟机+JVM高级特性与最佳实践(第二版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值