深入理解JVM读书笔记——自适应内存管理(二)

笔记书籍:JRockit权威指南深入理解JVM

作者:马库斯.希尔特 马库斯.拉杰格伦 著
翻译:曹旭东

3.4性能与伸缩

3.4.1线程局部分配

为分配对象而保留的区域被称为线程局部缓冲区(thread local area,TLA),每一个线程都有一个线程局部缓冲区。
正常情况下,在线程内的缓冲区中为对象分配内存要比直接 在需要同步操作的堆上分配内存快得多。
一般情况下,为了更好地利用缓存,达到更高的性能,TLA的大小介于16KB与128KB之间。当TLA被填满时,垃圾回收器会将TLA中的内容提升到堆中。因此,可以将TLA看作是线程中的新生代内存空间。

3.4.2更大的堆内存

垃圾回收的复杂程度通常与存活对象集合的大小相关,与堆的大小没什么关系。因此,在存活对象集合不变的情况下,可以让垃圾回收器使用更大的堆内存,这样做能够减缓内存碎片化的趋势,并且能够存储更多的存活对象。

*以下内容仅做简单概述,了解即可
1. 32位架构下 的4GB内存限制

在32位系统中 ,内存的最大寻址范围是4GB,但其他东西(例如操作系统本身)也会占用一部分空间。Windows操作系统要求其内核位于内存中端,无法开辟出一块连续的内存空间,大大限制了堆的使用 。
JRockit是目前唯一支持以非连续内存空间作为堆的JVM,需要记录一些额外的信息来 连接两块分离的堆内存空间。(性能较低,开销较大)

2. 64位架构

对于64位架构无须考虑内存空间被非应用程序所占去,内存空间较大 。但是在64位架构中,指针字节由4字节变为8字节,需要消耗更多的CPU缓存资源,所以64位架构下运行较慢。
压缩指针
在64位系统中,堆外的本地指针是系统运行的一部分,仍然是64位的,例如JNI用于引用Java对象的句柄。在代码中可能需要通过JNI转换,将压缩过的指针转换为普通的、系统长度的地址,或是做反向转换。例如垃圾回收器和本地代码可能会在内部使用本地指针 ,需要有相应的方法将压缩的指针转换为普通的系统指针,以及反向转换的方法。这个转换过程就称为指针压缩(reference compression)和指针解压缩(reference decompression)。
就JRockit来说,压缩指针之后的主要努力方向是最大化可用堆内存的大小和可存放在L1缓存中的对象数量。为了避免某些场景下可能出现问题,JRockit不会压缩局部栈帧中的指针,一般来说,代码生成器会在载入对象域后插入解压缩指针的代码,在存储对象域之前插入压缩指针代码,这样做虽然有性能损耗,但是极小忽略不计。

3.4.3缓存友好性

垃圾回收器应兼顾缓存友好性,当缓存大量丢失,应用程序性能则会大幅下降。
CPU中含有指令缓存和数据缓存(还有一些其他专用缓存)
缓存中包含有多个缓存行(cache-line),缓存行是CPU可访问的最小缓存单元。
当CPU从 内存中获取数据后,会将数据放入到缓存行中,以便将来使用时可以更快获取,这样会比从内存中再读取一次快上几个数量级。

在内存管理系统中,需要关注的问题有:对象存放、对齐和内存分配策略
1 预抓取
使用预抓取策略把即将使用到的数据预先加载到缓存中。在垃圾回收器中,使用TLA分配内存时,TLA实际上是被划分成很多很小内存块的,当使用某个内存块时,会预抓取与其相邻的内存块,这样当使用相邻内存块时就可以快速获取到所需数据。
2 数据存放
如果能预知在某段时间内,访问数据是按顺序或近似顺序的,那么预先将可能会访问到的数据放到同一缓存行中,根据局部性原理,可以获得更好的性能。
除了通过运行时反馈信息外,还可以通过获取其他静态信息来预测可能会使用到的数据,例如可以预先抓取某个对象引用到的其他对象,或者某个数组对象中所包含的元素
JVM预加载就是这个原理!!!!!!!!恍然大悟!!!!!!!!!

3.4.4 大内存页

内存分配是通过操作系统及其所使用的页表完成的。操作系统将物理内存划分成多个页来管理,从操作系统层面讲,页实际分配内存的最小单位。传统上,页的大小是以4KB为基本单位划分的。
进程所使用的的是虚拟地址空间,并非真正的物理地址。为了将虚拟页面转换为实际的物理内存地址,可使用名为旁路转换缓冲(translation lookaside buffer,TLB)的缓存来加速地址的转换操作。

从实现上看,如果页面的容量非常小的话,会导致频繁出现旁路转换缓冲丢失。
解决办法:将页面容量调大几个数量级。

3.4.5 自适应

JRockit可以在应用程序运行过程中,基于内存管理系统的反馈信息来调整垃圾回收的具体行为和相关参数,例如堆的大小,代的划分,甚至调整垃圾回收的整体策略。

3.5 近实时垃圾回收(只做了解)

3.5.1软实时与硬实时系统

硬实时系统是一种更加传统的实时系统,类似合成器或心脏起搏器之类,要求100%确定系统的行为。
软实时系统指运行时系统可以接受一定程度延迟,并控制暂停时间,这样即使软件系统仍然存在不确定性,单次的暂停时间也不会超过某个阈值。

3.5.2 JRockit Real Time

JRockit Real Time 主要特点就是可以不修改已有系统的情况下保证系统有稳定的延迟,即插即用,用户只需在命令行参数中指定期望的暂停时间。
软实时是JRockit Real Time的核心机制。
JRokic 如何实现如此高性能的垃圾回收?
——高效的并行执行
——细分垃圾回收过程,将之变成几个可回滚、可中断的子任务(work packet)
——高效的启发式算法
实现低延迟的关键仍是尽可能多让Java应用程序运行,保持堆的使用率和碎片化成都在一个较低的水平。
标记-清理算法。标记占用了90%的时间损耗,采用多个子任务共同协作模式,若暂停应用时间过长,则暂停并不废弃一个子任务,待到下次垃圾回收时再继续执行。
使用写屏障来跟踪系统中被 标记为脏的卡片数量,可以分析垃圾回收的行为。如果某个线程的写操作明显多于其他线程,那么就需要运行时系统对该线程特别注意,并做一些优化。

3.6内存操作相关的API

3.6.1析构方法

finalize()是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用它finalize()方法,让此对象处理它生前的最后事情(这个对象可以趁这个时机挣脱死亡的命运)。
如何自我救赎:
1.对象覆写了finalize()方法(这样在被判死后才会调用此方法,才有机会做最后的救赎);
2.在finalize()方法中重新引用到"GC Roots"链上(如把当前对象的引用this赋值给某对象的类变量/成员变量,重新建立可达的引用).
每个java对象包含一个finalize方法,用户可以重写该方法完成自定义析构操作。按照文档说明,finalize方法会在该对象被当做垃圾回收之前调用,因不知析构函数何时执行,导致资源无法重复利用,所以资源释放应该由程序员显式控制。
finalize方法可能被任意线在任意时间调用,无论该线程当前持有哪个对象的锁,都可以正常执行。会造成死锁,或违反Java语义的定义。
析构函数应避免使用。

3.6.2 Java中的引用

Java中存在多种引用,分为四种 :强引用、弱引用、虚引用、软引用、
几种对象引用类型位于java.lang.ref包下,均继承自java.lang.ref.Reference类。所有的Reference类型的实例都有一个get方法用于获取当前引用所指向的实际对象,如果对象是不可达的,则调用get方法则为Null。
1.弱引用
弱引用是指那些不足以使对象保留在内存中的引用。
弱引用常作为缓存的key用于java.util.WeakHashMap实例中。当JVM执行垃圾回收时,弱引用所指向的对象会被释放掉,这样可以防止无意识的内存泄漏,弱引用可以看做是Java中可用于防止内存泄漏的一种内建机制。
2.软引用
作为一种弱引用,软引用所指向的对象,垃圾回收器会尽可能将之保存在内存中,但当内存不足时,会首先将之回收掉。
3.虚引用
虚引用更常用于实现析构函数,用于取代finalize方法。与弱引用和软引用类似,它也是对普通对象的包装,只不过其get方法返回的永远是null。

访问虚引用需要以定期的间隔(或执行阻塞remove 操作)轮询java.lang.ref.Reference-Queue实例,以便得知目标对象是否要被回收掉了。由于虚引用的get只会返回null,所以无法获取到其所指向的真正对象的句柄并使其复活,也就避免了finalize方法存在的问题,又有了它的优点。

JRockit命令行参数:-XX:AllowSystemGC=False禁用System.gc方法。

3.7 JRockit中的内存管理

本节简单介绍与JRockit内存管理相关的基本命令行开关。
1、打印垃圾回收日志
-Xverbose:gc(或者-Xverbose:memory)
OC和YC分别表示老年代垃圾回收(old collection )和新生代垃圾回收(nursery collection)
2、设置堆的初始值和最大值
参数-Xmx和-Xms是所有JVM通用的标准参数,分别用于指定堆的最大值和初始值。如果没有设置,则堆的大小可能会在应用程序运行过程中,依据运行时反馈信息增大或缩小。以下面的配置为例:
java -Xms1024M -Xmx2048M
设置堆得初始值为GB,最大值为2GB。如果应用程序超出2GB,则会报OutOfMemoryError错误
3、设置垃圾回收期的执行目标
-XgcPrio:throughput:主要优化吞吐量,不关心响应时间。
-XgcPrio:pausetime:主要优化低延迟。
-XgcPrio:deterministic:该参数会启用JRockit Real Time,以消耗额外的系统资源换取极端的暂停时间。
以下配置启用了JRockit Real Time,并设置了暂停时间的上限:
java -XgcPrio:deterministic -XpauseTarget:5ms

4、指定垃圾回收策略
-Xgc系列参数可以用于指定垃圾回收策略
-Xgc:singlecon:不分代的并发垃圾回收(single generational concurrent).
-Xgc:gencon:分代式并发垃圾回收(generational concurrent)
-Xgc:singlepar:不分代的并行垃圾回收(single generational parallel)。
-Xgc:genpar:分代式并行垃圾回收(generational parallet)

5、压缩引用
下面的配置禁用了引用压缩,并强制JRockit使用原始本地指针来标识对象地址。
java -XXcompressedRefs:enable=false
下面的配置启用了引用压缩,并制定所支持的堆的大小是64GB:
java -XXcompressedRefs:enable=true,size=64GB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值