3.4 引用类型
Java中提供了4个级别的引用,强引用、软引用、弱引用和虚引用。这4个级别中,只有强引用是FinalReference类是包内可见。其他3种类型均为public,可以在应用程序中直接使用。
3.4.1 强引用
通过引用,可以对堆中的对象进行操作。在某函数中,当创建了一个对象,该对象被分配在堆中,通过这个对象的引用才能对这个对象进行操作。如
StringBuffer str= new StringBuffer("hello java");
假设以上代码是在函数体内运行的,那么局部变量str将被分配在栈上,而对象StringBuffer实例,被分配在堆上。局部变量str指向StringBuffer实例所在堆空间,通过str可以操作该实例,那么str就是StringBuffer的引用
此时,如果运行一个赋值语句
StringBuffer str1=str;
str所指向的对象也将被str1所指向。同时在局部空间上会分配空间存放str1变量。如下图。
上例中的两个引用,都是强引用,强引用具备以下特点。
- 强引用可以直接访问目标对象
- 强引用锁指向的对象在任何时候都不会被系统回收,JVM宁愿抛出OOM异常,也不回收强引用所指向的对象。
- 强引用可能导致内存泄漏
3.4.2 软引用
除了强引用,最强的就是软引用。可以通过java.lang.ref.SoftReference 使用软引用。一个持有软引用的对象,不会被JVM很快回收,JVM会根据当前堆的使用情况来判断何时回收。但堆使用率临近阀值时,才会去回收软引用。只要有足够的内存,软引用便可能在内存中存活相当长一段时间。因此,软引用可以用于实现对内存敏感的Cache.
5.2 JVM内存分配参数
5.3.1 设置最大堆内存
通过-Xmx 参数指定,最大堆指的是新生代和老年代的大小之和的最大值,它是Java应用程序的堆上限。
5.2.2 设置最小堆内存
使用-Xms 设置系统的最小堆空间。也就是JVM启动时,锁占据的操作系统内存大小。
Java应用程序在运行时,首先会被分配-Xms指定的内存大小,并尽可能尝试在这个空间内运行程序。但-Xms指定的内存大小确实无法满足引用程序时,JVM才会想操作系统申请更多的内存,直到内存大小达到-Xmx指定的最大内存为止,若超过最大内存,则抛出OutOfMemoryError异常。
如果-Xms的数值较小,那么JVM为了保证系统尽可能在指定内存范围内运行,就会更加频繁地进行GC操作,以释放失效的内存空间,从而会增加MinorGC 和Full GCde 次数,对系统性能产生一定的影响。
5.2.3 设置新生代
参数-Xmn 用于设置新生代的大小。设置一个较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为有很大的影响,新生代一般设置为整个堆空间的1/4或者1/3。
在Hot Spot虚拟机中,-XX:NewSize和-XX:MaxNewSize分别设置新生代的初始值和最大值。但是通常情况下-Xmn可以满足绝大部分应用的需要。
设置不同的-XX:NewSize和-XX:MaxNewSize可能会导致内存震荡,从而产生不必要的系统开销。
5.2.6 堆的比例分配
参数-XX:SurvivorRatio 用来设置新生代中,eden空间和s0空间的比例关系。s0和s1空间又被称为from空间和to空间。它们的大小是相同的,只能也是一样的。并在MinorGC后,会互换角色。
参数-XX:NewRatio可以设置新生代和老年代的比例。-XX:NewRatio=老年代/新生代
5.2.7 堆分配参数总结
与Java应用程序堆内存相关的JVM参数:
-Xms:设置Java应用程序启动时的初始堆大小。-Xmx:设置Java应用程序能获得的最大堆大小。-Xss:设置线程栈的大小-XX:MinHeapFreeRatio 设置堆空间最小空闲比例,但堆空间的空闲内存小于这个数值时,JVM便会扩展堆空间-XX:MaxHeapFreeRatio 设置堆空间的最大空闲比例,当堆空间的空闲内存大于这个数值时,便会压缩堆空间-XX:NewSize:设置新生代的大小-XX:NewRatio 设置老年代与新生代的比例,它等于老年代大小除以新生代大小-XX:SurvivorRatio 新生代中eden区与Survivor区的比例。-XX:PermSize 设置永久区的初始值
5.4 常用调优案例和方法
5.4.1 将新对象预留在新生代
由于Full GC的成本要远远高于Minor GC,因此尽可能将对象分配在新生代是一项明智的做法。虽然大部分情况下,JVM会尝试在eden区分配对象,但是由于空间紧张等问题,很可能不得不将部分年轻对象提前向老年代压缩。因此,在JVM参数调优中,可以为应用程序分配一个合理的新生代空间,以最大限度避免新对象直接进入老年代的情况。
/**
* @author study
* @version 1.0
* @date 2021/6/3 13:46
* -XX:+PrintGCDetails -Xmx20M -Xms20M
*/
public class PutInEden {
public static void main(String args[]) {
byte[] b1, b2, b3, b4;//定义变量
b1 = new byte[1024 * 1024];//分配 1 MB 堆空间,考察堆空间的情况。
b2 = new byte[1024 * 1024];
b3 = new byte[1024 * 1024];
b4 = new byte[1024 * 1024];
}
}
5.4.2 大对象进入老年代
5.4.4 稳定与震荡的堆大小
一般来说,稳定的堆大小是对垃圾回收有利的。获得一个稳定的堆大小的方法是使-Xmx和-Xms的大小一致,即最大堆和最小堆一样,如果这样设置,系统在运行时,堆大小是恒定的,稳定的堆空间可用减少GC的次数。因此,很多服务器都会将最大堆和最小堆设置为相同的数值。
但是,一个不稳定的堆也并不是毫无用处。稳定的堆大小虽然可以减少GC次数,但是同时也增加了每次GC时间。让堆大小在一个区间中震荡,在系统不需要使用大内存时,压缩堆空间,使GC应对一个较小的堆,可以加快单次GC的速度。基于这样的考虑,JVM还提供了两个参数用于压缩和扩展堆空间。
-XX:MinHeapFreeRatio:设置堆空间最小空闲比例,默认是40,当堆空间的空闲内存小于和这个数值时,JVM便会扩展堆空间
-XX:MmxHeapFreeRatio:设置堆空间最大空闲比例,默认是70,当堆空间的空闲内存大于这个数值时,JVM便会压缩堆空间,得到一个较小的堆。
当-Xms和-Xmx相等时,这两个自动扩展参数无效。
5.4.6 使用大页案例
在Solaris系统中,JVM可以支持大页的使用,使用大的内存分页可以增强CPU的内存寻址地址,从而提升系统的性能。
