4. 引用
Book b1 = new Book(); //等号左边叫引用,在栈里。右边叫对象,在堆里。
- 强引用(Strong Reference)、 90%
- 软引用(Soft Reference)、缓存
- 弱引用(Weak Reference)、
- 虚引用(Phantom Reference)4 种
reference
英
/ˈrefrəns/
n.
提及,谈到;参考,查阅;(引自书或诗歌的)引言,引文;引文的作者,参考书目;
adj.
参考的,用于查阅的;文献索引的,参照的
v.
列出……的参考书目;提及,提到;引用,参照(某书或某作者)
soft
adj.
软的,柔软的;硬度较低的,较软的;柔滑的,细嫩的;无棱角的,线条柔和的
adv.
温柔地;<美>轻轻地;无力地,愚蠢地
n.
柔性;柔软的东西;柔软部分
weak
英
/wiːk
adj.
虚弱的,无力的;不牢固的;懦弱的,无判断力的;缺乏政治(或社会)影响力的,
n.
穷人,弱者,病人(the weak)
phantom
英
/ˈfæntəm/
n.
幽灵,鬼魂;幻觉,幻象;渺茫;为诈骗而捏造的财务安排(或金融交易)
adj.
虚幻的,幻觉的;有名无实的,虚假骗人的;像鬼的,幽灵似的;(尤指罪犯)身份不明的
整体架构
Object
- Reference 老师说是强
- SoftReference 软
- WeakReference 弱
- PhantomReference 虚
- ReferenceQueue
1. Reference强引用:死都不收
强引用(默认支持模式)
当内存不足,JM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不
会对该对象进行回收,死都不收。
强引用是我们最常见的普通对象引用,
只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰
这种对象。在Java
中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一
个强引用。当一个对象被强引用变量引
用时,它处于可达
状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到
JVM也不会回收。因此强引用是造成Ja
va内存泄漏的主要
原因之一。
对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域
或者显式地将相应(强)引用赋值为null,
一般认为就是可以被垃圾收集的了(当然具体回收时机还是要看垃圾收集策略
Object o1 = new Object();//强引用
Object o2 = o1;//引用复制
o1 = null;//置空。只是改变了 o1 的指向。
System.gc();
//new Object() 还有指向 o2 ,不回收。即:o2 不为null。
System.out.println(o2);
2. Soft软引用:不够就收
- 内存足够的情况下 不收
- 尽量不要导致 OOM
- 比如:MyBatis 的代码
软引用是一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftRefere
nce类来实现,可以让对象豁免一些垃圾收集。
对于只有软引用的对象来说,
-
当系统内存充足时它不会被回收,
-
当系统内存不足时它会被回收。
软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够
用的时候就保留,不够用就回收!
Object o1 = new Object();
SoftReference<Object> s1 = new SoftReference<>(o1);
o1 = null;
//System.gc();
//System.out.println(s1.get());//内存够用,s1不会被回收
try {
//-Xms10m -Xmx10m
byte[] b = new byte[30 * 1024 * 1024];//30M
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(o1);
//内存不够用,变成null
System.out.println(s1.get());
}
3. Weak弱引用:一律回收
不管够不够用,一律回收
弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存期更知
短,
对于只有弱引用的对象来说,只要垃
圾回收机制一运行,不管JVM的内存空|
间是否足够,都会回收该对象占用的内存。
Object o1 = new Object();
WeakReference<Object> s2 = new WeakReference<>(o1);
o1 = null;
System.gc();
System.out.println(s2.get());//为null
使用场景
软引用和弱引用的适用场景
- MyBatis
你知道弱引用的话,能谈谈
WeakHashMap 吗?
假如有一个应用需要读取大量的本地图片:
如果每次读取图片都从硬盘
读取则会严重影响性能,
如果一次性全部加载到内存
中又可能造成内存溢出。
此时使用软引用可以解决这个问题
设计思路是:用一个HashMap来保存图片的路径和相应图片对象
关联的软引用之间的映射关系,在内存不足时,
JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了
OOM的问题。
Map<String, SoftReference<BitMap>> imageCache = new HashMap<>(); //BitMap位图索引,新redis的
- 如果用 弱引用,更不会 OOM
WeakHashMap
- 当 key 被置空,map中 对应的key 和 value 将会移除 。
- 就会腾出内存空间
WeakHashMap<Integer, String> map = new WeakHashMap<>();
Integer k = new Integer(1);
map.put(k, "111");
map.put(null, "222");
map.put(3, "222");
k = null;
System.gc();
//使用 HashMap,依然打出:{1=111}
//WeakHashMap 为:{null=222, 3=222}。1被溢出
System.out.println(map);
4. Phantom虚引用
虚引用需要java.lang.ref.PhantomReference类来实现。
顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的
生命周期。
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可
能被垃圾回收器回收,它不能单独使用也
不能通过它访问
对象,虚引用必须和引用队列(ReferenceQueue)联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对
象被finalize以后,做某些事情的机制。
PhantomReference的get方法总是返回null,因此无法访问对应的引用对象。
其意义在于说明一个对象已经进入finali
ization阶段,可以被
gc回收,用来实现比finalization机制更灵活的回收操作。
换句话说,设置虚引用关联的唯一目的
就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步嵌
的处理。
Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前
做必要的清理工作。
- 死缓(先放入 队列里),2年后执行,执行之前能做点什么。
- 类似 AOP的后置通知
- 做:对象回收的监控。
Class ReferenceQueue
我被回收前需要被引用队列保存下。
引用队列
Object o1 = new Object();
ReferenceQueue<Object> rq = new ReferenceQueue<>();
WeakReference<Object> wr = new WeakReference<>(o1, rq);
System.out.println(wr.get());
//gc之前,引用队列为 null
System.out.println(rq.poll());
o1 = null;
System.gc();
try {
//等一下,并行的GC
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//回收后为:null
System.out.println(wr.get());
//引用队列有值:java.lang.ref.WeakReference@2b80d80f
//对于 软 弱 虚 。gc真真正正 回收之前,会放入引用队列。gc()只是触发,真正GC的时候未知。
//我被回收前需要被引用队列保存下。
System.out.println(rq.poll());
虚引用
Object o1 = new Object();
ReferenceQueue<Object> rq = new ReferenceQueue<>();
PhantomReference<Object> pr = new PhantomReference<>(o1, rq);
/*public T get() {
return null;
}*/
//虚引用,直接返回null。用于监控对象的回收信息吧。后置通知,死之前:还有什么遗言?
System.out.println(pr.get());
//gc之前,引用队列为 null
System.out.println(rq.poll());
o1 = null;
System.gc();
try {
//等一下,并行的GC
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//虚引用,直接返回null。
System.out.println(pr.get());
//引用队列有值:java.lang.ref.WeakReference@2b80d80f
//对于 软 弱 虚 。gc真真正正 回收之前,会放入引用队列。gc()只是触发,真正GC的时候未知。
//我被回收前需要被引用队列保存下。
System.out.println(rq.poll());
引用队列总结
java提供了4种引用类型,在垃圾回收的时候,都有自己各自的特点。
Referenceoueue是用来配合引用工作的,没有ReferenceQueue一样可以运行。
创建引用的时候可以指定关联的队列,当GC释放对象内存的时候,会将引用加入到引用队
人列,
加果程序发现某个虑引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回
收之前采取必要的行动
这相当于是一种通知机制。
当关联的引用队列中有数据的时候,意味着引用指向的堆内存中的对象被回收。通过这种
中方式,JVM允许我们在对象被销毁后,
我们自己想做的事情。
GC Roots 和 四大引用总结
强引用 软引用 弱引用 虚引用
强引用:在垃圾回收 之外。
软引用:一半在 垃圾回收内,内存紧张的时候:回收软引用。
与根对象 无联系,统统都会被回收。
5. OOM
- -java.lang.StackOverflowError
- -java.lang.OutOfMemoryError: Java heap space
- -java.lang.OutOfMemoryError: GC overhead limit exceeded 超过开销限额
- java.lang.OutOfMemoryError: Direct buffer memory 直接缓冲存储器
- java.lang.OutOfMemoryError: unable to create new native thread’
- -java.lang.OutOfMemoryError: Metaspace 元空间
这个没有
- java.lang.OutOfMemoryError: Requested array size exceeds VM limit
栈溢出 StackOverflowError
- 栈溢出,Linux默认1024K,window系统,根据虚拟机的大小。调优是 最好设置为1024K
- 深度的方法调用
java.lang.StackOverflowError
private static void test() {
test();
}
//test();
Throwable
- Error 错误
- VirtualMachineError
- OutofMemoryError
- StackOverflowError
- VirtualMachineError
- Exception 异常
- Runtime Exception
OutOfMemoryError:堆溢出 Java heap space
- 对象太多,或 太大。
java.lang.OutOfMemoryError: Java heap space
-Xms10m -Xmx10m
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
byte[] b = new byte[1024 * 1024 * 11];
String str = "";
while (true) {
str += str + new Random().nextInt(11111111);
str.intern();
}
heap
英
/hiːp/
n.
(凌乱的)一堆;许多,大量;<非正式>破旧的汽车,老爷车
v.
堆积,堆放;对……大加赞扬(或指责等)
GC收不动 GC overhead limit exceeded
-java.lang.OutOfMemoryError: GC overhead limit exceeded 超过开销限额
-
GC over head limit exce eded
-
内存急剧上升,98% 都用作垃圾回收了,回收效果 特别糟糕。GC一直在运行。
VM参数配置演示
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m #直接内存改小。
# 2m,直接内存,我配置了2M,多运行一会
*GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回
收了不到2%的堆内存
*连续多次GC都只回收了不到2%的极端情况下才会抛出。假如不抛出 GC overhead limit 错
误会发生什么情况呢?
*那就是GC清理的这么点内存很快会再次填满,
迫使GC再次执行.这样就形成恶性循环,
*CPU使用率一直是100%,而GC却没有任何成果
- 事倍功半,也不到。
int i = 0;
List<String> list = new ArrayList<>();
try {
while (true) {
list.add(String.valueOf(i++).intern());
}
} catch (Throwable e) {
System.out.println("===========" + i);
e.printStackTrace();
throw e;
}
# 重GC 也收不动。
Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7048K->7048K(7168K)] 9096K->9096K(9728K)
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
直接内存不足 Direct buffer memory
- 直接缓冲存储器
- netty nio
- 元空间并不在虚拟机中,而是使用本地内存 。
配置参数:
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
故障现象:
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
//10891.0MB 我的 1/4 大概为 10G
System.out.println(sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024 + "MB");
ByteBuffer b = ByteBuffer.allocateDirect(6 * 1024 * 1024);
allocate
英
/ˈæləkeɪt/
v.
分配,分派,划拨
导致原因:
写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buf
ffer)的I/0方式,
它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuf
fer对象作为这块内存的引用进行操作。
- 引用在 JVM 里面,对象在 真实内存里
这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
- ByteBuffer.allocate(capability)第一种方式是分配JVM堆内存,属于GC管辖范围,由于需
要拷贝所以速度相对较慢 - ByteBuffer.allocteDirect( capability)第
二种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快。
但如果不断分配本地内存,堆内存很少使用,
那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,
这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemo
ryError,那程序就直接崩溃了。
-
JVM 的堆内存够用,根本不会启动GC
-
就是 JVM 默认最大的内存,初始化最大堆的内存,默认为 真实的 1/4
-
找到 java——jre——lib——rt.jar
- san\misc\VM.class
线程上限 unable to create new native thread
- 创建线程的上线已经达到了
高并发请求服务器时,经常出现如下异常:java.lang.OutOfNemoryError: unable to create n
ew native thread
准确的讲该native thread异常与对应的平台有关
导致原因:
- 你的应用创建了太多线程了,一个应用进程创建多个线程,超过系统承戴极限
- 你的服务器并不允许你的应用程序创建这么多线程, Linux系统默认允许单个进程可以创建
的线程数是1024个,- 所以:手写线程池,最大为 1024。老师测试最多可创建到940。非root用户,才有这个限制。
- 我的虚拟机root用户 创建了 thread 32638。我的 线程最高配置为:31117
- 所以:手写线程池,最大为 1024。老师测试最多可创建到940。非root用户,才有这个限制。
你的应用创建超过这个数量,就会报java.Lang.OutOfMemoryError: unable to create new na
tive thread
解决办法:
1.想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代
码将线程数降到最低
2.对于有的应用,确实需要创建很多线程,远超过Linux系统的默认1024个线程的限制,可以通过修
改Linux服务器配置,扩大linux认限制
Exception in thread "main" java.lang.IllegalThreadStateException
Thread t = new Thread();
t.start();
t.start();
Exception in thread "main" java.lang.IllegalMonitorStateException
Lock l = new ReentrantLock();
l.unlock();
java.lang.OutOfMemoryError: unable to create new native thread
for (int i = 0; ; i++) {
new Thread(() -> {
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "" + i).start();
}
- 调整 默认创建的 数量
ulimit -u
31117
vim /etc/security/limits.d/90-nproc.conf #我的是20
* soft nproc 4096
root soft nproc unlimited
添加:
zhangsan用户名 soft nproc 4096
元空间 Metaspace
java.lang.OutOfMemoryError: Metaspace 元空间
- 元空间使用 本地机器内存
- 就是 方法区,装的是类模板
使用java -XX:+PrintFlagsInitial命令查看本机的初始化参数,
(大约20.8M),20.79
-XX:Metaspacesize为21810376
了VM参数
-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
Java 8及之后的版本使好Metaspace来替代永久代。
Metaspace是方法区在HotSpot中的实现,它与持久代最大的区别在于:Metaspace并不在
虚拟机内存中而是使用本地内存
也即在java8中, cLasse metadata(the virtual machines internal presentation o
f Java class),被存储在叫做
Metaspace l的native memory
永久代(java8后被原空间Metaspace取代了)存放了以下信息:
- 虚拟机加载的类信息。rt.jar包的
- 常量池
- 静态变量
- 即时编译后的代码
模拟Metaspace空间溢出,我们不断生成类往元空间灌,类占据的空间总是会超过Mwetaspac
e指定的空间大小的
- 8M,创建了 258个类
发生了异常:258
org.springframework.cglib.core.CodeGenerationException: java.lang.OutOfMemoryError-->Metaspace
static class OOMTest {
}
public static void main(String[] args) {
int i = 0;
try {
while (true) {
i++;
//cglib的 创建反射类
Enhancer e = new Enhancer();
e.setSuperclass(OOMTest.class);//不停的创建这个静态的类(不停的生成模板)
e.setUseCache(false);
e.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invoke(o, args);
}
});
e.create();
}
} catch (Throwable e) {
System.out.println("发生了异常:" + i);
e.printStackTrace();
}
}
6. 垃圾回收器
垃圾回收器,谈谈你的理解?垃圾回收的方式有哪些?
四大垃圾收集算法和收集器
Gc算法(引用计数/复制拷贝/标记清除/标记整理)是内存回收的方法论,垃圾业
仗集器就是算法落地实现。
因为目前为止还没有完美的收集器出现,更加没有万能的收集器,只
是针对具体应用最合适的收集器,
进行分代收集
4种主要垃圾收集器
- serial 串行回收
- Parallel 并行回收
- CMS 并发标记清除
- G1
- 截止到 java10 以前。11,12 有了 ZGC,比G1 更新
serial
英
/ˈsɪəriəl/
adj.
顺序排列的,一连串(系列)的;(重罪案犯或重案)连环的,连续的;连续播放的,
n.
(电视或广播)连续剧,(报纸上的)连载故事;(图书馆的)期刊
parallel
英
/ˈpærəlel/
adj.
平行的;相似的,同时发生的;(计算机)并行的;并联的
n.
(人或事物的)相似的手法,共同点;相似的人(或物);(地球的)纬线,纬圈;(印刷)平行符号
v.
与……相似;与……同时发生;与……并行;与……相当,比得上
adv.
与……平行
初识
串行Serial
串行垃圾回收器(Serial)
它为单线程环境设计且只使用
个线程进行垃圾回收,会暂停所有的用户线程。
- 暂停所有用户线程,GC 线程,回收完 才能继续。
- 服务员 打扫所有的环境,打扫好后,客户才能过来用餐。
所以不适合服务器环境
垃圾收集器的原始实现
- 垃圾收集器的原始实现
- 适用于能够承受短暂停顿的应用程序
- 会停止应用程序(通常称为“stop the world”事件)
- 占用内存空间比较小
并行Parallel
并行垃圾回收器(Parallel)
多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科
学计算/大数据处理前台处理等
弱交互场景
-
和 串行差不多,就是 垃圾回收线程,变成了多个。
-
一个扫地工人不够,多个扫地工人。
-
也使用“stop the world”方法
-
Parallel 收集器运行时有多个线程执行垃圾收集操作
-
这是 JVM 中的默认垃圾收集器,也被称为 吞吐量收集器
-
以通过使用各种合适的 JVM 参数进行调优,
- 例如吞吐量、暂停时间、线程数和内存占用
并发CMS
Concurrent Mark Sweep
并发垃圾回收器(CMS)
用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执
行),不需要停顿用户线程
互联网公司多用它,适用对响应时间有要求的场景
-
你们先到 1号桌吃饭,先把二号桌 打扫干净
-
一边有程序运行,一边有垃圾回收。
-
分成4步,有2步,还是会暂停一下。
-
Concurrent Mark Sweep(CMS)收集器
-
这个收集器在 Java8 已过时,并在 Java14 中被移除。
-
应用程序将暂停两次
总结这3个
串行(Serial) vs并行(Parallel)
-
并行:相当于 多道处理
-
并行:在单核CPU下并行可能更慢;
STW(Stop-the-world) vs并发(Concurrent)
- 并发:暂停 当前正在垃圾回收的线程。
. STW:暂停整个应用,时间可能会很长;
·并发(Concurrent):更为复杂,GC可能会抢占应用的CPU;
G1
G1垃圾回收器
garbage
英
/ˈɡɑːbɪdʒ/
n.
<美>垃圾,废物;<美>垃圾箱;<非正式>废话,无聊的东西;无用信息
- jdk7 开始验证 ,java8 诞生,java9的默认垃圾回收为G1,java11变成了 ZGC
- G1 垃圾收集器旨在替代 GMS。具备并行、并发以及增量压缩,且暂停时间较短。
G1垃圾回收器将堆内存分割成不同的区域然后并发的对其
进行垃圾回收
Epsilon 收集器
是在 Java11 中引入的,是一个 no-op (无操作)收集器。它不做任何实际的内存回收,只负责管理内存分配。
epsilon
英
/ˈepsɪlɒn;
n.
希腊语字母表的第五个字母;小的正数;(一系列项目、分类中的)第五
lion
英
/ˈlaɪən/
n.
狮子;名人;勇猛的人;社交场合的名流
Shenandoah 收集器
- Shen an do ah
Shenandoah 是在 JDK12 中引入的,是一种 CPU 密集型垃圾收集器。
- 它会进行内存压缩,立即删除无用对象并释放操作系统的空间。
shenandoah
美
/ˌʃenənˈdoʊə/
n.
谢南多厄河(美国弗吉尼亚州河流);谢南多厄河谷(地名);情人渡,水手瑶(歌曲名)
ZGC 收集器
JDK11 引入,在 JDK12 改进。在 JDK15,ZGC 和 Shenandoah 都被移出了实验阶段。
-
低延迟需要和大量堆空间使用而设计
-
允许当垃圾回收器运行时 Java 应用程序继续运行。
-
ZGC(Z Garbage Collector) 是一款性能比 G1 更加优秀的垃圾收集器。
-
JDK 16 发布后,GC 暂停时间已经缩小到 1 ms 以内。毫秒。
-
内存多重映射
-
把不同的虚拟内存地址映射到同一个物理内存地址上
-
染色指针
-
读屏障
-
如何查看
jps -l
jinfo -flag UseSerialGC xx
-XX:-UseSerialGC #默认没开,这是序列化GC吧。默认为并行。布尔类型。如下
jinfo -flag UseParallelGC 9572
-XX:+UseParallelGC
java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=802858688
-XX:MaxHeapSize=12845739008
-XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation
-XX:+UseParallelGC
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
7.怎么查看服务器默认的垃圾业
器是那个?
生产上如何配置垃圾收集器的
谈谈你对垃圾收集器的理解?
默认的垃圾回收有哪些 7种
概述
垃圾收集器就来具体实现这些GC算法并实现内存回收。
不同厂商、不同版本的虚拟机实现差别很大,HotSpot中包含的收集器如下
图所示:
java的gc回收的类型主要有几种
- UseSerialGC(1)
- UseSerialOldGC 串行老年区GC, java8时 就淘汰了 (7) 串行老年代
- UseConcMarkSweepGc,(6)并发标记清除
- UseParNewGC(在 新生代 区的)(3)并行新生代
- UseParallelGC (2)并行回收
- UseParallelOldGC(在老年代使用的),(5)并行老年代
- UseG1GC (4) G1
-
串行 新生代
-
串行 老年代
-
并行新生代,串行老年代
-
并行新生代
-
并行老年代
-
并发标记清除 CMS
-
G1
Young Gen
- Serial Copying 对应:UseSerialGC(1)串行复制
- Parallel Scavenge 对应:UseParallelGC(2)并行回收
- ParNew 介于上面两个之间。对应:UseParNewGC(3)并行新生代
G1 在中间,两个都能用。对应:UseG1GC(4) G1
Old Gen
-
Serial MSC (Serial Old) 废弃了 (7) 串行老年代
-
Parallel Compacting(Parallel Old) 对应:UseParallelOldGC (5)并行老年代
-
CMS 是UseConcMarkSweepGc java14被移除。(6)并发标记清除
scavenge
英
/ˈskævɪndʒ/
vt.
以……为食;打扫;排除废气
参数说明
DefNew
Default New Generation
# 默认 新生代,使用串行。
Tenured
Old
# 默认 老年代,使用串行。
ParNew
Parallel New Generation
#新生代用 并行
PSYoungGen #generation 新生代用 并行,老年代也使用并行。
Parallel Scavenge
ParOldGen
Parallel Old Generation
tenure
英
/ˈtenjə(r)/
n.
(土地的)居住权,保有权;(尤指大学教师的)终身职位,长期聘用;(尤指重要政治职务的)任期,任职
v.
授予……终身职位(尤指教师、讲师职位)
tenured
adj.
(美)享有终身职位的
v.
授予……终身职位(tenure 的过去分词)
generation
英
/ˌdʒenəˈreɪʃ(ə)n
n.
一代(人);一代人的时间;(产品发展的)代;产生
general
adj.
总体的,普遍的;一般的,常规的;大致的,
n.
上将,将军;半军事化管理的宗教组织
gentleman
n.
(称呼或指男子,尤其是不认识的)先生;彬彬有礼的人,有教养的人
server/Client
分别是什么意思
server/Client模式分
1适用范围:只需要掌握Server
模式即可,Client模式基本不会用
2操作系统:
2.1 32位Window操作系统,不论硬件如何都默认使用Client的JVM
模式
2.2 32位其它操作系统,2G内存同时有2个cpu以上用Server模式,
低于该配置还是Client模式
2.3 64位only server模式
java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
新生代
串行GC(serial)/(Serial Copying)
- 老年代会用:Serial Old(MSC)
并行GC(ParNew)
-
新生代多个,老年代 一个。
-
Serial Old 会用这个吗?这个不太清楚。应该会自动切换到 CMS 吧。
-
老年代会用:CMS
并行回收GC(Parallel)/(Pararallel Scavenge)
- 老年代会用:Parallel Old
- 老年代会用,这个已经没了: Serial Old(MSC)
串行Serial
【串行收集器:Serial收集器】
一句话:一个单线程的收集器,在进行垃圾收集时候,必须暂停其他所有的工
作线程直到它收集结束。
- 通常称为“stop the world”事件
串行收集器是最古老,最稳定以及效率
高的收集器,只使用一个线程去回收但其在进行垃圾收集过程中可能会产生较
长的停顿(
Stop-The-World”状态)。虽然在收集垃圾过程中需要暂停所有其他的工作线
程,但是它简单高效,对于限定单个CP
U环境来说,没
有线程交互的开销可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器
器依然是java虚拟机运行在Client模式下默认的新生代垃
圾收集器。
对应JVM参数是:-XX:+UseSerialGC
开启后会使用:Serial(&foung区用)+ Serial Old(Old区用)的收集器组合
表示:新生代、老年代都会使用串行回收收集器,新生代使用复制算法,老年
代使用标记-整理算法
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC
# (DefNew+Tenured)
# 配置了-XX:+UseSerialGC
DefNew 就是 新生代会用一个默认的处理器 UseSerialGC。老年代就变成了:tenured
[GC (Allocation Failure) [DefNew: 2747K->320K(3072K), 0.0012966 secs] 2747K->937K(9920K), 0.0013281 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation
eden space 2752K
from space 320K
to space 320K,
tenured generation
Metaspace
并行GC(ParNew)
- 老年代会用:CMS?
- 新生代多个线程,老年代一个线程。
ParNew(并行)收集器
一句话:使用多线程进行垃圾回收,
在垃圾收集时,会Stop-the-World暂停其
他所有的工作线程直到它收集结束。
ParNew收集器其实就是Serial收集器新生代的并行多线程版本,最常见的应用
场景是配合老年代的CMS GC工作,其
余的行为和
Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也要暂停所
有其他的工作线程。它是很多java虚拟
机运行在Server
模式下新生代的默认垃圾收集器。
常用对应JVM参数:-XX:+UseParNew
GC启用ParNew收集器,只影响新生
代的收集,不影响老年代
- 这个 New 代表 新生代
开启上述参数后,会使用:ParNew(Young区用)+ Serial Old的收集器组合,新
生代使用复制算法,老年代采用标记-整理算法
- 养老区 还是一个线程 回收的线程。
- 所以会 暂停两次。
但是,ParNew+Tenured这样的搭配,java8已经不再被推荐
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC
(DefNew+Tenured)
# -XX:+UseParNewGC 本次使用这个
(ParNew+Tenured)
-XX:ParallelGCThreads限制线程数量,默认开启和CPU数目相同的线程数
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
并行回收GC(Parallel Scavenge)
并行回收GC(Parallel)/(Pararallel Scavenge)
- 新生代,老年代,也是 多个 gc线程。都是并行收集。
- 这也是 默认的 gc
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
# -XX:+UseParallelOldGC
(PSYoungGen+ParoldGen)
#generation 和 Parallel Scavenge
并行收集器组合Parallel Scavenge + Parallelold
Parallel Scavenge收集器类似ParNew也是一个新生代垃圾收集器,使用复制
算法,也是一个并行的多线程的垃圾收集
器,俗称吞吐
量优先收集器。一句话:串行收集器在新生代和老年代的并行化
它重点关注的是:
可控制的吞吐量(Thoughput=运行用户代码时间/(运行用户代码时间+垃圾收集时间),也即比如程序运行100分钟,垃圾l
收集时间1分钟,
吞吐量就是99%)。高吞吐量意味着高效利用CPU的时间,它多用于在后台运
算而不需要太多交互的任务。
自适应调节策略也是ParallelScavenge
收集器与ParNew 收集器的一个重要区
别。(自适应调节策略:虚拟机会根据当
前系统的运行情
况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间
(-XX:MaxGCPauseMillis)或最大的吞吐量。
常用JVM参数:-XX:+UseParallelGC或-XX:+UseParallelOldGC(可互相激活)
使用Parallel Scanvenge收集器
开启该参数后:新生代使用复制算法,
老年代使用标记-整理算法
多说一句:-XX:ParallelGCThreads=数字N
表示启动多少个GC线程
cpu>8 N = 5/8
- 启动 总CPU的 5/8
cpu<8 N = 实际个数
老年代
串行GC(Serial Old)/(Serial MSC)
并行GC(Parallel Old)/(Parallel MSc)
并发标记清除GC(CMS) Concurrent Mark Sweep
Parallel Old
Parallel Old收集器是Parallel Scaveng
e的老年代版本,使用多线程的标记-整尹
理算法,Parallel Old收集器在JDK1.6才开
F始提供。
在JDK1.6之前,新生代使用ParallelScavenge 收集器只能搭配年老代的S
erial Old 收集器,只能保证新生代的吞
吐量优先,无法保
证整体的吞吐量。在JDK1.6之前(Parallel Scavenge + Serial Old )
Parallel Old正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统
;对吞吐量要求比较高,JDK1.8后可以优
先考虑新生代
Parallel Scavenge和年老代Parallel Old收集器的搭配策略。在JDK1.8及后
(Parallel Scavenge + Parallel Old )
JVM常用参数:
-XX:+UseParallelOldGC使用Parallel Old收集器,设置该参数后,新生代Parallel+老年代parallel Old
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
(PSYoungGen+ParoldGen)
#generation 和 Parallel Scavenge
#配了-XX:+UseParallelOldGC,果然新生代 也变成并行的了。不加 系统默认的也是这个。
jinfo -flag UseParallelGC 24168
-XX:+UseParallelGC
#-XX:+UseConcMarkSweepGC
( par new generation + concurrent mark-sweep generation )
#-XX:+UseG1GC
#-XX:+UseserialOldGC 没有了
** CMS
- Concurrent Mark Sweep(CMS)收集器
- 这个收集器在 Java8 已过时,并在 Java14 中被移除。
垃圾收集算法——标记清除法
- 落地的实现产品:CMS
(Mark-Sweep)
算法分成标记和清除两个阶段,先标记出要回收的对象,
然后统一回收这些对象。
-
CMS收集器(Concurrent Mark Sweep:并发标记清除)是一种以获取最短
回收停顿时间为目标的收集器。
适合应用在互联网站或者BIS系统的服务器上,这类应用尤其重视服务器的!
响应速度,希望系统停顿时间最短。 -
CMS非常适合堆内存大、CPU核数多的服务器端应用,也是G1出现之前大型
应用的首选收集器。 -
并发标记清除收集器组合ParNew + CMS + Serial Old
Concurrent Mark Sweep并发标记清除,并发收集低停顿,并发指的是与用户
线程一起执行
开启该收集器的JVM参数:-XX:+UseConcMarkSweepGC开启该参数后
会自动将-XX:+UseParNewGC打开
开启该参数后,**使用ParNew(Young区用)+ CMS(Old区用) + Serial Old **的收
集器组合,Serial Old将作为CMS出错的后备收集器
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
#-XX:+UseConcMarkSweepGC
( par new generation + concurrent mark-sweep generation )
#-XX:+UseG1GC
#-XX:+UseserialOldGC 没有了
- InitialMark 初始标记,停顿
只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。
- concurrent Mark / Preclean 2和4步骤,可以交替的执行
并发标记(CMs concurrent mark)和用户线程一起、
进行GC Roots跟踪的过程,和用户线程一起工作,不需要暂停工作线程。主要标记过程,标记全部对象
- Remark 重新标记
- 相当于 二次确认
为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需
要暂停所有的工作线程。
由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正
- concurrent Sweep
并发清除(CMs concurrent sweep)和用户线程一起
清除GC Roots不可达对象,和用户线程一起工作,不需要暂停工作线程。基于标记结果,直接清理对象
由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,
所以总体上来看CMS收集器的内存回收和用户线程是一起并发地执行。
Initial Mark:日志为:CMS Initial Mark;
标记GCRoot可以直达的对象,耗时短。
Concurrent Mark:日志为:CMS-concurrent-mark-start CMS-concurrent-mark
从第一步标记的对象出发,并发地标记可达对象。
Remark :日志为:CMS Final Remark
重新进行标记,修正Concurrent Mark期间由于用户程序运行而导致对象关系间的变化及新创建的对象。耗时短。
Concurrent Sweep:日志为:CMS-concurrent-sweep-start
并行地进行无用对象的回收。
优
- 并发收集低停顿
缺
-
并发执行,对cPu资源压力大
-
采用的标记清除算法会导致大量碎片
由于并发进行,CMS在收集与应用线程会同时会增加对堆内存的占用,也就是说
CMS必须要在老年代
堆内存用尽之前完成垃圾回收,否则CMS回收失败时,将触发担保机制,
- 保底的是:串行 老年代(单线程的)
串行老年代收集器将会以
STW的方式进行一次GC,从而造成较大停顿时间
标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐步耗尽,最后将不得不通过
担保机制对堆内存进行压
缩。CMS也提供了参数-XX:CMSFullGCsBeForeCompaction(默认0,即每次都进行内存整理)
来指定多少次CMS收集之
后,进行一次压缩的Full GC。
Serial Old
Serial Old是Serial垃圾收集器老年代版本,它同样是个单线程的收集器,使用标记-整理算法,这个收集
器也主要是运行在 Client默认
的java虚拟机默认的年老代垃圾收集器。
在Server模式下,主要有两个用途(了解,版本已经到8及以后):
1.在JDK1.5之前版本中与新生代的Parallel Scaveng
e 收集器搭配使用。(Parallel Scavenge + Serial Old )
2.作为老年代版中使用CMS收集器的后备垃圾收集方案。
(理论知道即可,实际中已经被优化掉了,没有了。)
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldGC
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Unrecognized VM option 'UseSerialOldGC'
Did you mean '(+/-)UseSerialGC'?
选择合适的垃圾收集器
组合的选择
.单CPU或小内存,单机程序
-XX:+UseSerialGC
多CPU,需要最大吞吐量,如后台计算型应用-XX:+UseParallelGC或者
-XX:+UseParallelOldGC
多CPU,追求低停顿时间,需快速响应如互联网应用-XX:+UseConcMarkSweepGc
-XX:+ParNewGC
参数
新生代垃级收集器
新生代算法
老年代垃圾收集器
老年代算法
-XX:+UseSerialGc
SerialGc
复制
SerialOldGc
标整
-XX:+UseParNewGc
ParNew
复制
SerialOldGc
标整
-XX:+UseParallelGC /-XX:+UseParallelOldGC
Parallel[Scavenge]
复制
Parallel Old
标整
-XX:+UseConcMarkSweepGc
ParNew
复制
CMS + Serial Old的收集器组合
(Serial Old作为CMS出错的后备
收集器)
标清 (Sweep 标记清除)
-XX:+UseG1Gc
G1整体上采用标记-整理算法
局部是通过复制算法,不会
产生内存碎片。
G1
- garbage-first
- java17 默认的也是G1
- G1 jdk7 开始验证 ,java8 诞生,java9的默认垃圾回收为G1,java11变成了 ZGC
garbage
英
/ˈɡɑːbɪdʒ/
n.
<美>垃圾,废物;<美>垃圾箱;<非正式>废话,无聊的东西;无用信息
rubbish
n.
垃圾,废弃物;毫无价值的东西,劣质产品;废话,瞎说
以前收集器特点
年轻代和老年代是各自独立且连续的内存块;
年轻代收集使用单eden+S0+S1进行复制算法; (幸存0区和1区)
老年代收集必须扫描整个老年代区域;
都是以尽可能少而快速地执行cc为设计原则。
是什么
G1是什么
G1[(Garbage-First)收集器,是一款面向服务端应用的收集器
从官网的描述中,我们知道G1是一种服务器端的垃圾收集器,应用在多处理器和大容量内存环境中,在实
现高吞吐量的同时,尽
可能的满足垃圾收集暂停时间的要求。另外,它还具有以下特性:
-
像CMS收集器一样,能与应用程序线程并发执行。
-
整理空闲空间更快。
-
需要更多的时间来预测GC停顿时间。
-
不希望牺牲大量的吞吐性能。
-
不需要更大的Java Heap。
G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现的更出色:
G1是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。
G1的Stop The World(STW)更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。
CMS垃圾收集器虽然减少了暂停应用程序的运行时间,但是它还是存在着内存碎片问题。于是,为了去除
内存碎片问题,同时又保留
CMS垃圾收集器低暂停时间的优点,JAVA7发布了一个新的垃圾收集器-G1垃圾收集器。
G1是在2012年才在jak1.7u4中可用。oracle官方计划在jdk9中将G1变成默认的垃圾收集器以替代CMS。它
是一款面向服务端应用的收集
器,主要应用在多CPU和大内存服务器环境下,极大的减少垃圾收集的停顿时间,全面提升服务器的性能,
逐步替换java8以前的CMS收集器。
主要改变是Eden,Survivor和Tenured等内存区域不
再是连续的了,而是变成了一个个大小一样的region
**每个region从1M到32M不等。**一个region有可能属于Eden,Survivor或者Tenured内存区域。
- 不在区分 伊甸园,幸存区,养老区
特点
1:G1能充分利用多CPU、多核环境硬件优势,尽量缩短STW。stop the world
2:G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。
3:宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域(Region),可以近似理解为
一个围棋的棋盘。
4:G1收集器里面讲整个的内存区都混合在一起了,但其本身依然在小范围内要进行年轻代和老年代的区分
,保留了新生代和老年代,
但它们不再是物理隔离的,而是一部分Region的集合且不需要Region是连续的,也就是说依然会采用不同
的GC方式来处理不同的区域。
5:G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要完全独立的
survivor(to space)堆做复制
准备。G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换;
- 尽量把空的内存区域,联合在一起。一块区域,一会是 伊甸园 一会可能是养老区。
底层原理
Region区域化垃圾收集器
最大好处是化整为零,避免全内存扫描,
只需要按照区域来进行扫描即可。
区域化内存划片Region,整体编为了一些列不连续的内存区域,避免了全内存区的GC操作。
核心思想是将整个堆内存区域分成大小相同的子区域(Region),在JVM启动时会自动设置这些子区域的大在堆的使用上,G1并不要求对象的存储一定是物理上连续的只要逻辑上连续即可,每个分区也不会固定地
.为某个代服务,可以按需在年
轻代和老年代之间切换。启动时可以通过参数-XX:G1HeapRegionSize=n可指定分区大小(1MB~32MB,且
必须是2的幂),默认将整堆划
分为2048个分区。
大小范围在1MB~32MB,最多能设置2048个区域,也即能够支持的最大内存为:32MB*2048= 65536MB=64G内存
G1将新生代,老年代的物理空间划分取消了。
G1算法将堆划分为若干个区域(Region)
- Eden
Survivor
old
Humongous 超大对象区。把 一些块 联合起来装。
G1算法将堆划分为若干个区域( Region) ,它仍然属于分代收集器
- 比如 某一个块,现在是 Eden 或 Survivor ,依然才用 复制算法。
这些Region的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。
这些Region的一部分包含老年代,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有
CMS内存碎片问题的存在了。
- 该复制的复制,该转移的转移,该回收的回收,为空后,完成这一块的压缩。
在G1中,还有一种特殊的区域,叫Humongous(巨大的)区域
如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象默认直接会被分配在年老代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来
存储。为了能找到连续的H区,有时候不得不启动Full Gc。
- 餐厅用饭,桌子拼在一起,让8个过来。
humongous
英
/hjuːˈmʌŋɡəs/
adj.
巨大无比的,极大的
回收步骤
G1收集器下的Young Gc
针对Eden区进行收集,Eden区耗尽后会被触发,主要是小区域收集+形成连续的内存块,避
免内存碎片
Eden区的数据移动到Survivor区,假如出现Survivor区空间不够*,Eden区数据会部会晋升
到Old区
Survivor区的数据移动到新的Survivor区,部会数据晋升到Old区
- 如果 幸存区 不够装,选有一个新的空闲区域,做幸存区。
- 即:多个幸存区,伊甸园区 收集后 合并成 一个新的幸存区
- 幸存区 可以晋升为 养老区
*最后Eden区收拾干净了,GC结束,用户的应用程序继续执行。
初始标记:只标记GC Roots能直接关联到的对象
- 对应日志:(initial-mark)
并发标记:进行GC Roots Tracing的过程
- [Gc concurrent-root-region-scan-star]
[Gc concurrent-root-region-scan-end,0.0000146 secs] - [Gcconcurrent-mark-start
[Gc concurrent-mark-end,0.0012045 secs]
最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
- [Finalize Marking,
筛选回收:根据时间来进行价值最大化的回收
-
[GC worker start (ms ) :
-
和 CMS 一样,只是在 一块里面。 拿 新生区呢??这里应该说的是养老区吧。
配置
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
-XX:+UseG1GC
-XX:G1HeapRegionSize=n:设置的G1区域的大小。值是2的幂,范围是1MB到32M。目标是根据最小的Java堆大小划分出约2048个区域。
-XX:MaxGCPauseMillis=n:最大Gc停顿时间,单位毫秒,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间
-XX:InitiatingHeapOccupancyPercent=n:堆占用了多少的时候就触发Gc,默认为45
-XX:ConcGCThreads=n:并发Gc使用的线程数
-XX:G1ReservePercent=n:设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险,默认值是10%
三步归纳:开始G1+设置最大内存+设置最大停顿时间
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100
# -XX:+UseZGC
//新生区 养老区 合并了
Heap
garbage-first heap total 10240K, used 3463K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000)
region size 1024K, 1 young (1024K), 0 survivors (0K)
Metaspace used 3442K, capacity 4620K, committed 4864K, reserved 1056768K
class space used 372K, capacity 392K, committed 512K, reserved 1048576K
和 CMS 相比优势
比起cms有两个优势:
1)G1不会产生内存碎片。
**2)是可以精确控制停顿。**该收集器是把整个堆(新生代、老生代)划分成多个固定大小的区域,
每次根据允许停顿的时间去收集垃圾最多的区域。
结合微服务调优java -server
java -Xms1024m -Xmx1024m -XX:UseG1GC -jar springboot2019-1.0—SNAPSHOT.war
java -server jvm的各种参数 -jar 第一步上面的jar或war包名字
jps -l
jinfo -flags xx #打印自己配置的参数,我的报错
Attaching to process ID 11284, please wait...
Exception in thread "main" java.lang.reflect.InvocationTargetException
JVM面试题
1.JVM垃圾回收的时候如何确定垃圾﹖是否知道什么是GC Roots
⒉.你说你做过JvM调优和参数配置,请问如何盘点查看JVM系统默认值
3.你平时工作用过的VM常用基本配置参数有哪些?
4.强引用、软引用、弱引用、虚引用分别是什么?
Java中默认声明的就是强引用
5.请谈谈你对OOM的认识
6.GC回收算法和垃圾收集器的关系?另外,串行收集/并行收集/并发收集/STW是什么?
7.怎么查看服务器默认的垃圾收集器是那个?
生产上你是如何配置垃圾收集器的?谈谈你的理解?
8.G1垃圾收集器21
9.生产环境服务器变慢,诊断思路和性能评估谈谈?
10.假如生产环境出现CPu占用过高,请谈谈你的分析思路和定位(13
11.对于JDK自带的Jvm监控和性能分析工具用过哪些?一般你是怎么用的?
- jconsole.exe
- jps,类似Linux系统里面的Ps -ef命令
- jinfo
- jmap
- jstat
- jstack
12.JVM的字节码指令接触过吗?