- 1.认识GC roots?
- 2、引用可达与引用不可达
- 3、java中可用作GC roots的对象
- 4、JVM调优和参数配置
- 如何理解两个经典参数: -Xms和-Xmx
- JVM默认值
- 5、强引用、软引用、弱引用、虚引用分别时什么?
- 认识weakHashMap
- 6、OOM错误分析
- 1.StackOverflowError
- 2、OutOfMemoryError: java heap space
- 3、OutofMemoryError: GC overhead limit exceeded
- 4、OutofMemoryError: Direct buffer memory
- 5、OutofMemoryError: unable to create new native Thread
所谓GC roots,就是一组必须活跃的引用,从这个GC Roots对象的起始点开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。即给定一个集合的引用从根出发,通过引用关系遍历对象图,能被遍历到的对象被判定为存活,没有被遍历到的就被判定为死亡。
2、引用可达与引用不可达从GC roots开始为起点向下引用的对象,是引用可达对象,反之是引用不可达对象。
- 虚拟机栈中引用的对象(也就局部变量)
- 方法区中的类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(native方法)引用的对象
-Xms 初始化内存大小,默认物理内存的1/64,等价于-XX:InitialHeapSize
-Xmx 最大分配内存,默认是物理内存的1/4,等价于-XX:MaxHeapSize
-Xss 设置单个线层栈的大小,一般默认是512k ~ 1024k,等价于-XX:ThreadStackSize
-Xmn 设置年轻代大小,一般采用默认即可
-XX: metaspaceSize 设置元空间的大小
案例: -Xms10m -Xmx 10m -XX:metaspaceSize = 1024m -XX: +PrintFlagsFianl
元空间的本质和永久代类似,都是对JVM规范中的方法区的实现,不过元空间与永久代之间最大的区别是:
元空间并不在虚拟机中,而是使用本地内存。
因此,默认的情况下,元空间的大小受本地内存的限制。
-XX:+PrintGCDetails
-XX: SurvivorRatio 设置新生代中eden和s0/s1空间的比例,默认-XX: SurvivorRatio=8,Eden:s0:s1 = 8:1:1
-XX: NewRatio 配置年轻代与老年代在堆结构的占比,默认-XX: NewRatio = 2,新生代占1,老年代占2,年轻代占整个堆的1/3.
-XX:MaxTenuringThreshold 设置垃圾的最大年龄,如果等于0的话,则年轻代对象不经过Survivor区,直接进入年老区,默认的大小是15
JVM参数类型:
- 配置参数(java -version 、 java -help 、 java -showversion )
- X参数
- XX参数
一般通过jps、jinf命令查看进程的状态,并赋值新的大小
一、Boolean类型
-XX: + 表示开启
-XX: - 表示关闭
二、KV设置类型
公式: -XX:属性key = 属性值value
case: -XX:metaspaceSize = 128m
-XX:MaxTenuringThreshold = 15
metaspaceSize: 表示元空间的大小
MaxTenuringThreshold: 经过多少次后升入到养老区
三、jinfo
jsp -l 表示查看当前线程的编号,相当于linux ps|ef grep
jinfo -flag 配置项 **(*表示进程编码) 查看正在运行中的java程序
案例: jinfo -flag InitialHeapSize 8232 JPS查看了8232进程编号堆的初始化大小
jinfo -flags java进程编号 查看进程所有的参数
如何理解两个经典参数: -Xms和-Xmx
JVM默认值-Xms 等价于 - XX: InitialHeapSize 初始化堆大小,InitialHeapSize默认值是系统内存的1/64,即,如果电脑内存为16G,那么它的大小时16G的1/64.
-Xmx 等价于:-XX: MaxHeaPSize 最大化堆大小,InitialHeapSize默认值是系统内存的1/4,即,如果电脑内存为16G,那么它的大小时16G的1/4.
= 表示的是系统初始值。:=表示人为修改过或者JVM 加载修改过
- -XX: +PrintFlagsInitial -version(主要查看初始默认值)
- -XX:+PrintFlagsFinal -version (主要查看修改更新)
- -XX:PrintCommandLineFlags
关系图如下:
强引用(默认支持)
当内存不足的时候,JVM开始垃圾回收,对于强引用,就算是出现了OOM异常,也不会对该对象进行回收,所以强引用是造成java内存泄露的主要原因之一。在java最常见的就是强引用,把对象赋给一个引用变量,这个引用变量就是一个强引用。例如: Student stdent = new Student();就是一个强引用。
软引用
软引用时一种相对强引用弱化的一些引用,需要用java.lang.ref.SoftReference类来实现,当系统内存充足的时候,不会存在内存回收,当系统内存不充足的时候,会存在内存回收,因此,一般用于高速缓存就有用到软引用,内存够得时候就保留,不够的时候就回收。
应用实例: 假如有一个应用需要读取大量的本地图片:
如果每次读取的图片都是从硬盘读取则会严重影响性能,如果一次性加载全部内存中又可能造成内存溢出。此时,可采用软引用。
解决思路:
用一个hashmap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足的时候,JVM会自动回收这些缓存图片对象所占用的空间,从而有效避免OOM异常。
弱引用
弱引用需要用java.lang.ref.SoftReference类来实现,它比软引用的生存期更短。一般来说,只要有垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。
认识weakHashMapWeakHashMap比较适合做缓存。
WeakHashMap实现了Map接口,基于hashtable实现,在这种Map中,key的类型是WeakReference。如果对应的key被回收,则这个key指向的对象会被从Map容器中移除。
WeakReference是“弱键”实现的哈希表。它这个“弱键”的目的就是:实现对“键值对”的动态回收。当“弱键”不再被使用到时,GC会回收它,WeakReference也会将“弱键”对应的键值对删除。
import java.util.HashMap;
import java.util.WeakHashMap;
public class hashDemo {
public static void main(String[] args) {
hashTest();
System.out.println("=====================");
myWeakHashMap();
}
private static void hashTest() {
WeakHashMap hashMap = new WeakHashMap<>();
Integer key = new Integer(2);
String value = "WeakHashMap";
hashMap.put(key,value);
System.out.println(hashMap);
key = null;
System.out.println(hashMap);
System.gc();
System.out.println(hashMap+"t"+hashMap.size());
}
private static void myWeakHashMap() {
HashMap hashMap = new HashMap<>();
Integer key = new Integer(1);
String value = "HashMap";
hashMap.put(key,value);
System.out.println(hashMap);
key = null;
System.out.println(hashMap);
System.gc();
System.out.println(hashMap+"t"+hashMap.size());
}
}
结果:
虚引用
他和其他引用不同,虚引用不会决定对象的生命周期,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器所回收。因此,他不能单独使用,虚引用必须和引用队列联合使用。
==作用:==主要跟踪对象被垃圾回收的状态,并且它的get()方法总是返回null,所以无法访问对应的引用对象。
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class myReferenceQueue {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
ReferenceQueue
结果:
结论:
虚引用只要被执行gc()了,相当于持有虚引用的对象被放入引用队列中,当队列被干掉之前,它可以做点其他事情,这就是虚引用的通知机制。
6、OOM错误分析OOM下报的是错误,不是异常信息。
public class stackOverFlowErrorDemo {
public static void main(String[] args) {
stackOverFlow();
}
private static void stackOverFlow() { //at com.resource.faceTest.stackOverFlowErrorDemo.stackOverFlow
stackOverFlow();
}
}
2、OutOfMemoryError: java heap space
public class javaHeapSpaceDemo {
public static void main(String[] args) {
String str = "study";
while (true){
str += str + new Random().nextInt(1111) + new Random().nextInt(2222);
str.intern();
}
}
}
3、OutofMemoryError: GC overhead limit exceeded
GC回收时间过长会抛出OOM异常。过长定义的是,超过98%的时间来做GC并且回收了不到2%的堆内存,连续多次GC都只回收不到2%的极端情况下会抛出异常。
4、OutofMemoryError: Direct buffer memory表示直接内存挂了。
导致原因: 写NIO程序经常使用ByteBuffer来读取或者写入数据,因为NIO是一种基于通道(channel)与缓冲区(Buffer)的I/O方式。
内存分配方式:
1、ByteBuffer.allocate(capability) 该分配方式时基于JVM堆内存,属于GC管辖范围,但是需要拷贝所以速度相对较慢。
2、ByteBuffer.allocateDirect(capability) 该分配方式是OS本地内存,不属于GC管辖范围,由于不需要在内存拷贝所以速度相对较快。
5、OutofMemoryError: unable to create new native Thread错误原因:native Thread 异常与对应的平台有关
导致原因:
创建太多的线程了,一个应用进程创建多个线程,超过了系统承载极限。linu系统默认允许单个进程可以创建的线程数量是1024个
解决方案:
1、想办法降低你的应用程序创建线程的数量,分析应用是否真的需要创建这么多的线程,如果不需要,通过修改代码降低创建线程的数量。 2、可以通过修改linux服务器配置,罗达linux默认限制
6、OutofMemoryError: metaspace
造成原因: 不断将生成的类往元空间里放入,类占据的空间总是会超过metaspace指定的大小。
metaspace是方法区在HotSpot中实现,他和持久层最大的区别在于: metaspace并不在虚拟机内存中而是使用本地内存。



