JVM及GC知识点总结

目录

 

一、JVM概述

二、JVM如何确定垃圾及GC Roots的理解

三、JVM的参数类型及设置

四、强引用、软引用、弱引用、虚引用的理解

五、常见OOM异常

六、GC垃圾回收算法和垃圾收集器

七、Linux服务器变慢排查步骤

 


一、JVM概述

①jvm结构

1.Class files ---------------------------------------类文件

2.Class loader ------------------------------------类装载器子系统

3.Runtime Data Area ---------------------------运行时数据区

3.1 Java stack ------------------------------------(线程私有)Java栈

3.2 Native Method Stack -----------------------(线程私有)本地方法栈

3.3 Program Counter Register ----------------(线程私有)程序计数器

3.4 Method Area ----------------------------------(线程公有)方法区

3.5 Heap --------------------------------------------(线程公有)堆

4.Native Interface --------------------------------本地方法接口

4.1本地方法库 -------------------------------------本地方法库

5.Execution Engine ------------------------------执行引擎

②常见垃圾回收算法

1.引用计数:引用即+1,一般不采用

2.复制:年轻代(Eden--->From<--->To)

3.标记清除:产生空间碎片

4.标记整理:标记清除升级版,移动整理需要成本

二、JVM如何确定垃圾及GC Roots的理解

1.内存中不再使用的空间就是垃圾

2.通过引用计数法或者枚举根节点做可达性分析来确认垃圾是否可以回收

2.1枚举根节点做可达性分析就是判断GC Roots对象是否被使用

GC Roots对象:

①虚拟机栈中对象的引用

②方法区中类静态属性的引用

③方法区中常量对象的引用

④本地方法栈中JNI(Native方法)对象的引用

三、JVM的参数类型及设置

1.JVM的参数类型

1.1标配参数:

java -version

java -help

java -showversion

1.2X参数

解释执行:java -Xint -version

编译执行:java -Xcomp -version

(默认)混合执行:java -Xmixed -version

1.3XX参数

查看当前进程信息

jps -l

jinfo -flags 进程号

是否打印GC收集细节:

-XX:+PrintGCDetails

-XX:-PrintGCDetails

是否使用串行垃圾收集器:

-XX:+UseSerialGC

-XX:-UseSerialGC

年轻代中eden和S0/S1的空间占比

-XX:SurvivorRatio=8--->默认,eden:s0:s1=8:1:1

年轻代大小

-Xmn

年轻代和老年代的堆结构占比

-XX:NewRatio=2--->默认1:2

晋升老年代转换阈值,即垃圾的最大年龄

-XX:MaxtenuringThreshold=15

元空间大小设置

-XX:MetaspaceSize=128m

堆大小及最大值

-Xms--->-XX:IntianlHeapSize---->初始大小:物理内存1/16

-Xmx--->-XX:MaxHeapSize------->初始大小:物理内存1/4

单个线程大小

-Xss--->-XX:ThreadStackSize---->一般默认为512-1024K

查看初始默认值

java -XX:+PrintFlagsInitial

查看修改更新值

java -XX:+PrintFlagsFinal

=标识默认值,:=标识修改后的值

快速查看修改的主要参数值

java -XX:+PrintCommandLineFlags

四、强引用、软引用、弱引用、虚引用的理解

1.强引用

普通对象,即使发生OOM也不会被垃圾回收。

示例

Object Strong = new Object();  --->o1引用指向实例对象

2.软引用

内存充足,不会被 回收;内存不够,就会被回收

适用场景:读取大量本地图片缓存到内存,内存不够就释放资源。

示例

//-Xms5m -Xmx5m -XX:+PrintGCDetails 手动造成OOM
System.out.println("软引用");
Object strong = new Object();
SoftReference<Object> soft = new SoftReference<>(strong);
strong = null;
try {
    byte[] bytes = new byte[30 * 1024 * 1024];
} finally {
    System.out.println("strong:" + strong);
    System.out.println("soft:" + soft.get());
}

3.弱引用

不管内存是否充足,都会被回收

使用场景:读取大量本地图片缓存到内存,只要垃圾回收就释放资源。

示例

System.out.println("弱引用");
Object strong = new Object();
WeakReference<Object> weak = new WeakReference<>(strong);
System.out.println("strong:" + strong);
System.out.println("soft:" + weak.get());
strong = null;
System.gc();
System.out.println("strong:" + strong);
System.out.println("soft:" + weak.get());

4.虚引用

本身就是不存储任何数据,返回null,虚引用必须和引用队列(ReferenceQueue)一起使用,一般配合监控垃圾回收

System.out.println("虚引用");
Object object = new Object();
ReferenceQueue<Object> ref = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(object, ref);
System.out.println("object: " + object);
System.out.println("虚引用: " + phantomReference.get());
System.out.println("应用队列" + ref.poll());
System.out.println("垃圾回收====");
object = null;
System.gc();
System.out.println("object: " + object);
System.out.println("虚引用: " + phantomReference.get());
System.out.println("应用队列" + ref.poll());

五、常见OOM异常

Error----------->错误

Exception---->异常

1.java.lang.StackOverflowError

示例:循环调用,栈空间不足,导致栈溢出

public void test1() {
    System.out.println("java.lang.StackOverflowError=======");
    method01();
}
static void method01() {
    method01();
}

2.java.lang.OutOfMemoryError: Java heap space

示例:new一个超过堆内存大小的对象,导致堆内存不足

//-Xms30m -Xmx30m
System.out.println("java.lang.OutOfMemoryError: Java heap space=======");
byte[] bytes = new byte[30 * 1024 * 1024];

3.java.lang.OutOfMemoryError: GC overhead limit exceeded

示例:不断创建新对象,导致GC无法正常回收

//-Xms10m -Xmx10m
System.out.println("java.lang.OutOfMemoryError: GC overhead limit exceeded=======");
List<String> list = new ArrayList<>();
int i = 0;
try {
    while (true) {
        list.add(String.valueOf(i));
    }
} catch (Exception e) {
    System.out.println("-----> i : " + i);
    e.printStackTrace();
    throw e;
}

4.java.lang.OutOfMemoryError: Direct buffer memory

示例:直接内存溢出,一般NIO程序经常出现

//-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
System.out.println("java.lang.OutOfMemoryError: Direct buffer memory =======");
long maxDirectMemory = VM.maxDirectMemory() / 1024l / 1024l;
System.out.println("系统最大直接内存:" + maxDirectMemory + "M");
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);

5.java.lang.OutOfMemoryError: unable to creat new native thread

示例:服务器线程限制,导致不能创建更多线程

进入linux系统,查看能开启的最大线程数,代表root用户没有限制,子用户最大4096,可自行手动修改

[root@v2ray limits.d]# pwd
/etc/security/limits.d
[root@v2ray limits.d]# ls
20-nproc.conf
[root@v2ray limits.d]# more 20-nproc.conf 
# Default limit for number of user's processes to prevent
# accidental fork bombs.
# See rhbz #432903 for reasoning.

*          soft    nproc     4096
root       soft    nproc     unlimited
[root@v2ray limits.d]# 

System.out.println("java.lang.OutOfMemoryError: unable to creat new native thread =======");
for (int i = 0; i >= 0; i++) {
    System.out.println("num--->" + i);
    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(1l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, String.valueOf(i)).start();
}

6.java.lang.OutOfMemoryError: Metaspace

示例:元空间不足,Metaspace并不在虚拟机内存中,而是使用本地内存

//-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

System.out.println("java.lang.OutOfMemoryError: Metaspace =======");
int num = 0;
while (true) {
    num++;
    try {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(OOMTest.class);
        enhancer.setUseCache(false);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return methodProxy.invokeSuper(o, args);
            }
        });
        enhancer.create();
    } catch (Exception e) {
        System.out.println("创建类超出元空间限制:" + num);
        e.printStackTrace();
    }
}

六、GC垃圾回收算法和垃圾收集器

1.分类

垃圾回收算法:引用计数、复制、标记清除、标记整理。

垃圾收集器:串行垃圾收集器(Serial)、并行垃圾收集器(Parallel)、并发垃圾收集器(CMS)、G1垃圾收集器。

2.具体实现

2.1串行垃圾回收器Serial

①单线程环境,垃圾回收时暂停用户线程,不适合服务器,更不适合高并发服务

②程序--->GC--->程序--->GC...

2.2并行垃圾回收器Parallel

①多线程并行工作,垃圾回收时暂停所有用户线程,适合大数据等弱交互场景

②程序--->GC--->程序--->GC...

2.3并发垃圾回收CMS(Concurrent Mark Sweep并发标记清除)

①多线程工作,垃圾回收时可不暂停用户线程,适合大型互联网公司

②程序--->GC(Mark)--->程序/GC(Mark/PreClean)--->GC(Remark)--->程序/GC(Sweep)...

③-XX:+UseConcMarksSweepGC:并发执行,低停顿;CPU压力大,产生大量碎片,一旦堆内存耗费前未完成GC则触发Serial Old进行垃圾收集,导致系统长时间停顿。

2.4G1垃圾回收器

①将堆内存分割成多个区域,然后并发工作

②java -server-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar XXX.jar

3.查看本地垃圾回收器

java -XX:+PrintCommandLineFlags

-XX:InitialHeapSize=198604096 -XX:MaxHeapSize=3177665536 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-Use
LargePagesIndividualAllocation -XX:+UseParallelGC

4.java垃圾回收器--->Java8默认:UseParallelGC

新生代收集器 Serial ParNew Parallel Scavenge
老年代收集器 Serial Old Parallel Old CMS
整堆收集器 G1

5.G1垃圾收集器

5.1旧版本收集器特点:

年轻代和老年代是各自独立且连续的内存块

年轻代收集使用eden+s0+s1进行复制算法

老年代收集时必须扫描整个老年代区域
特点都是少而快速的执行GC

5.2G1的特点

把内存划分成多个独立的子区域(Regin)
物理上不区分年轻代和老年代,只有逻辑上的分代概念,可按需切换
整体上采用标记整理算法,局部通过复制算法,不会产生内存碎片
充分利用多CPU、多核优势,缩短STW(Stop the work),可配置停顿时间

5.3G1执行步骤:初始标记--->并发标记--->最终标记--->筛选回收

七、Linux服务器变慢排查步骤

yum install -y sysstat

pidstat -d 采样间隔 -p 进程号

1.整机性能查看:

top--->查看load average(1/5/15分钟平均负载)、%CPU、%MEM

2.CPU查看:

vm -n 2 3--->查看us、sy内存消耗占比,2秒钟采样一次,共3次

3.内存查看:

free -m

4.硬盘查看:

df -h

5.硬盘IO查看:

iostat -xdk 2 3--->2秒钟采样一次,共3次

6.网络IO查看:

ifstat

7.进程命令查看:

jps -l

ps -ef | grep java | grep -v grep

8.线程号查看:

ps -mp 进程号 -o THREAD,tid,time

9.前60行错误代码输出,精准定位问题代码行数

jstack 进程号 | grep tid(小写线程号16进制转换值) -A60

 

发布了59 篇原创文章 · 获赞 13 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/u012725623/article/details/105488838