JVM 内存结构
GC 的作用域
常见的垃圾收集算法
JM垃圾回收的时候如何确定垃圾?是否知道什么是 GC Roots?
什么是垃圾?
简单的说就是内存中已经不再被使用到的空间就是垃圾
引用计数算法
枚举根节点做可达性分析(根搜索路径)
那么问题来了,哪些可作为GC Roots的对象
JVM常用基本配置参数有哪些?
在jdk各个版本之间稳定,很有大的变化
X 参数了解即可
1、Boolean 类型
公式:-XX:+或者- 某个属性,+表示开启、-表示关闭
示例
2、KV键值对类型
公式:-XX:属性key=属性值value
示例
题外话:jinfo 如何查看当前运行程序的配置
公式 1:jinfo -flag 配置项 进程编号
公式 2:jinfo -flags 进程编号
示例
题外话:两个经典参数 -Xms和-Xmx
-XX:+PrintFlagsInitial:主要查看初始默认值
公式:
示例
-XX:+PrintFlagsfinal:主要查看修改更新
公式:java -XX:+PrintFlagsFinal -version
示例
PrintFlagsFinal举例:运行Java命令的同时打印出参数
-XX:+PrintCommandLineFlags:打印命令行参数
公式:java -XX:+PrintCommandLineFlags -version
C:\Users\Heygo\Desktop\Interview>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=266620736 -XX:MaxHeapSize=4265931776 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocatio
n -XX:+UseParallelGC
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
基础知识复习
/**
* @ClassName HelloGC
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 15:12
* @Version 1.0
*/
public class HelloGC {
public static void main(String[] args) {
long totalMemory = Runtime.getRuntime().totalMemory(); // Java 虚拟机中的内存总量
long maxMemory = Runtime.getRuntime().maxMemory(); // Java 虚拟机试图使用的最大内存量
System.out.println("TOTAL_MEMORY:" + totalMemory / (double) 1024 / 1024 + "MB");
System.out.println("MAX_MEMORY:" + maxMemory / (double) 1014 / 1024 + "MB");
}
}
常用参数
-Xms:初始大小内存,默认为物理内存1/64,等价于-XX:InitialHeapSize
-Xmx:最大分配内存,默认为物理内存1/4,等价于-XX:MaxHeapSize
-Xss:设置单个线程栈的大小,等价于-XX:ThreadStackSize,一般默认为512~1024K;0代表默认出厂值
-Xmn:设置年轻代大小
-XX:MetaSpaceSize:设置元空间大小
-XX:+PrintGCDetails:输出详细GC收集日志信息。
-XX:SurvivorRatio:设置新生代中Eden和S0/S1空间的比例
-XX:NewRatio:配置年轻代与老年代在堆结构的占比
-XX:MaxTenuringThreshold:设置垃圾最大年龄
典型设置案例:可以和面试官闲聊的案例
-Xms4096m -Xmx4096m -Xss1024k -XX:MetaSpaceSize=512m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC
强引用、软引用、弱引用、虚引用分别是什么?
强、软、弱、虚
强引用(默认支持模式)
软引用的含义
软引用代码示例
示例 1:内存够用不会回收软引用对象
/**
* 内存够用的时候就保留,不够用就回收
*
* @ClassName SoftReferenceDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 21:54
* @Version 1.0
*/
public class SoftReferenceDemo {
public static void main(String[] args) {
softRef_Memory_Enough();
}
public static void softRef_Memory_Enough() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc(); // 进行 GC
System.out.println(o1); // 强引用变量为 null 必被回收
System.out.println(softReference.get()); // 内存够用不会回收软引用对象
}
}
java.lang.Object@4554617c
java.lang.Object@4554617c
null
java.lang.Object@4554617c
示例 2:内存不够用必定回收软引用对象
/**
* 内存够用的时候就保留,不够用就回收
*
* @ClassName SoftReferenceDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 21:54
* @Version 1.0
*/
public class SoftReferenceDemo {
public static void main(String[] args) {
//softRef_Memory_Enough();
softRef_Memory_NotEnough();
}
/*
JVm配置,故意产生大对象并配置小的内存,让内存不够用,导致OOM,看软引用的回收情况
-Xms5m -Xmx5m -XX:+PrintGCDetails
*/
public static void softRef_Memory_NotEnough() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
try {
byte[] bytes = new byte[30 * 1024 * 1024];
} catch (Throwable e) {
e.printStackTrace();
} finally {
System.out.println(o1);
System.out.println(softReference.get());
}
}
}
[GC (Allocation Failure) [PSYoungGen: 1024K->504K(1536K)] 1024K->640K(5632K), 0.0008207 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
java.lang.Object@4554617c
java.lang.Object@4554617c
[GC (Allocation Failure) [PSYoungGen: 1403K->504K(1536K)] 1539K->752K(5632K), 0.0007773 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 504K->504K(1536K)] 752K->768K(5632K), 0.0005913 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 504K->0K(1536K)] [ParOldGen: 264K->637K(4096K)] 768K->637K(5632K), [Metaspace: 3426K->3426K(1056768K)], 0.0055154 secs] [Times: user=0.09 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 637K->637K(5632K), 0.0002769 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) java.lang.OutOfMemoryError: Java heap space
at com.Heygo.SoftReferenceDemo.softRef_Memory_NotEnough(SoftReferenceDemo.java:46)
at com.Heygo.SoftReferenceDemo.main(SoftReferenceDemo.java:18)
[PSYoungGen: 0K->0K(1536K)] [ParOldGen: 637K->620K(4096K)] 637K->620K(5632K), [Metaspace: 3426K->3426K(1056768K)], 0.0056716 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
null
null
Heap
PSYoungGen total 1536K, used 92K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)
eden space 1024K, 9% used [0x00000000ffe00000,0x00000000ffe173f8,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 4096K, used 620K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)
object space 4096K, 15% used [0x00000000ffa00000,0x00000000ffa9b138,0x00000000ffe00000)
Metaspace used 3487K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 382K, capacity 388K, committed 512K, reserved 1048576K
弱引用的含义
弱引用需要用java.lang.ref.WeakReference类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存。
弱引用代码示例
/**
* @ClassName WeakReferenceDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 22:09
* @Version 1.0
*/
public class WeakReferenceDemo {
public static void main(String[] args) {
Object o1 = new Object();
WeakReference<Object> weakReference = new WeakReference<>(o1);
System.out.println(o1);
System.out.println(weakReference.get());
o1 = null;
System.gc();
System.out.println("...............");
System.out.println(o1);
System.out.println(weakReference.get());
}
}
java.lang.Object@4554617c
java.lang.Object@4554617c
...............
null
null
你知道弱引用的话,谈谈WeakHashMap
/**
* @ClassName WeakHashMapDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 22:14
* @Version 1.0
*/
public class WeakHashMapDemo {
public static void main(String[] args){
myHashMap();
System.out.println("========");
myWeakHashMap();
}
private static void myHashMap(){
HashMap<Integer,String> map = new HashMap<>();
Integer key = new Integer(1);
String value = "HashMap";
map.put(key,value);
System.out.println(map);
// key 置为 null ,关 HashMap 毛事啊,HashMap 已经将数据保存至 Node 节点中了
key = null;
System.out.println(map);
System.gc();
System.out.println(map);
}
private static void myWeakHashMap(){
WeakHashMap<Integer,String> map = new WeakHashMap<>();
Integer key = new Integer(2);
String value = "WeakHashMap";
map.put(key,value);
System.out.println(map);
key = null;
System.out.println(map);
System.gc();
System.out.println(map);
}
}
{1=HashMap}
{1=HashMap}
{1=HashMap}
========
{2=WeakHashMap}
{2=WeakHashMap}
{}
虚引用的作用
引用队列:我(虚引用)被回收前需要被引用队列保存下
引用队列代码示例
/**
* @ClassName ReferenceQueueDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 22:30
* @Version 1.0
*/
public class ReferenceQueueDemo {
public static void main(String[] args) throws InterruptedException{
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(o1,referenceQueue);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println("=============");
o1 = null;
System.gc();
Thread.sleep(500);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
}
}
java.lang.Object@4554617c
java.lang.Object@4554617c
null
=============
null
null
java.lang.ref.WeakReference@74a14482
虚引用代码示例
/**
* @ClassName PhantomReferenceDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 22:30
* @Version 1.0
*/
public class PhantomReferenceDemo {
public static void main(String[] args) throws InterruptedException {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o1,referenceQueue);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
System.out.println("=================");
o1 = null;
System.gc();
Thread.sleep(500);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
}
}
java.lang.Object@4554617c
null
null
=================
null
null
java.lang.ref.PhantomReference@74a14482
软引用、弱引用适用场景
Map<String,SoftReference<Bitmap>> imageCache = new HashMap<String,SoftReference<Bitmap>>();
GC Roots 和四种引用类型的总结
面试题:请谈谈你对OOM的认识
java.lang.StackOverflowError
/**
* @ClassName StackOverflowErrorDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 22:48
* @Version 1.0
*/
public class StackOverflowErrorDemo {
public static void main(String[] args){
stackOverflowError();
}
private static void stackOverflowError() {
stackOverflowError();
}
}
Exception in thread "main" java.lang.StackOverflowError
java.lang.OutOfMemoryError:Java heap space
/**
* @ClassName JavaHeapSpaceDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 22:53
* @Version 1.0
*/
public class JavaHeapSpaceDemo {
public static void main(String[] args){
String str = "seu";
while(true){
str += str + new Random().nextInt(11111111)+new Random().nextInt(22222222);
str.intern();
}
}
}
-Xms10m -Xmx10m
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at com.Heygo.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:17)
java.lang.OutOfMemoryError:GC overhead limit exceeded
/**
* @ClassName GCOverheadDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 23:02
* @Version 1.0
*/
public class GCOverheadDemo {
public static void main(String[] args) {
int i = 0;
List<String> list = new ArrayList<>();
try {
while (true) {
list.add(String.valueOf(++i).intern());
}
} catch (Throwable e) {
System.out.println("***************i:" + i);
e.printStackTrace();
throw e;
}
}
}
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7076K->7076K(7168K)] 9124K->9124K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0304580 secs] [Times: user=0.17 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7078K->7078K(7168K)] 9126K->9126K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0294555 secs] [Times: user=0.11 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7080K->7080K(7168K)] 9128K->9128K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0303605 secs] [Times: user=0.08 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7081K->7081K(7168K)] 9129K->9129K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0273443 secs] [Times: user=0.11 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7083K->7083K(7168K)] 9131K->9131K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0323289 secs] [Times: user=0.13 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7085K->7085K(7168K)] 9133K->9133K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0282554 secs] [Times: user=0.25 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7087K->7087K(7168K)] 9135K->9135K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0279839 secs] [Times: user=0.22 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7088K->7088K(7168K)] 9136K->9136K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0268396 secs] [Times: user=0.03 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7090K->7090K(7168K)] 9138K->9138K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0275986 secs] [Times: user=0.14 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7092K->7092K(7168K)] 9140K->9140K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0279635 secs] [Times: user=0.14 sys=0.00, real=0.03 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7094K->7094K(7168K)] 9142K->9142K(9728K), [Metaspace: 3472K->3472K(1056768K)], 0.0259208 secs] [Times: user=0.22 sys=0.01, real=0.03 secs]
***************i:145410
[Full GC (Ergonomics) java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.toString(Integer.java:403)
at java.lang.String.valueOf(String.java:3099)
at com.Heygo.GCOverheadDemo.main(GCOverheadDemo.java:20)
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.toString(Integer.java:403)
at java.lang.String.valueOf(String.java:3099)
at com.Heygo.GCOverheadDemo.main(GCOverheadDemo.java:20)
java.lang.OutOfMemoryError:Direct buffer memory
导致原因:
ByteBuffer.allocate(capability)
第一种方式是分配JVM堆内存,属GC管辖范围,由于需要拷贝所以速度相对较慢ByteBuffer.allocateDirect(capability)
第二种方式是分配OS本地内存,不属于GC管辖范围,由子不需要内存拷贝所以速度相对较快。代码示例:查看 MaxDirectMemory
/**
*
* @ClassName DirectBufferMemoryDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/9 23:18
* @Version 1.0
*/
public class DirectBufferMemoryDemo {
public static void main(String[] args) {
System.out.println("配置的maxDirectMemory: " + (sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024) + "MB");
try {
Thread.sleep(300);
} catch (Exception e) {
e.printStackTrace();
}
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
}
}
配置的maxDirectMemory: 3618.0MB
演示 DirectBuffer OOM
代码同上
JVM 参数
-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
配置的maxDirectMemory: 5.0MB
[GC (Allocation Failure) [PSYoungGen: 2035K->488K(2560K)] 2035K->792K(9728K), 0.0007089 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (System.gc()) [PSYoungGen: 564K->496K(2560K)] 868K->912K(9728K), 0.0008965 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 496K->0K(2560K)] [ParOldGen: 416K->705K(7168K)] 912K->705K(9728K), [Metaspace: 3483K->3483K(1056768K)], 0.0051785 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:694)
at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at com.Heygo.DirectBufferMemoryDemo.main(DirectBufferMemoryDemo.java:22)
结论:
写 NIO 和 Netty 程序时要小心
java.lang.OutOfMemoryError:unable to create new native thread
高并发请求服务器时,经常出现如下错误 java.lang.OutOfMemoryError:unable to create new native thread
,准确地讲,该 native thread 异常与对应的平台有关
导致原因:
java.Lang.outofMemoryError:unable to create new native thread
解决办法:
非root用户登录Linux系统测试
package com.atguigu.Interview.study..jvm.oom;
/**
* @ClassName UnableCreateNewThreadDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/10 12:59
* @Version 1.0
*/
public class UnableCreateNewThreadDemo {
public static void main(String[] args) {
for (int i = 1; ; i++) {
System.out.println(i);
new Thread(() -> {
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
java -d . UnableCreateNewThreadDemo.java
java com.atguigu.Interview.study..jvm.oom.UnableCreateNewThreadDemo
终止程序
ps -ef|grep java
kill -9 pid
服务器级别调优参数
ulimit -u
vim /etc/security/limits.d/90-nproc.conf
java.lang.OutOfMemoryError:Metaspace
代码示例
/**
* 模Metaspace空间溢出,我们不断生成类往元空间灌,类占据的空间总是会超过Metaspace指定的空间大小的
*
* @ClassName MetaspaceOOMTest
* @Description TODO
* @Author Heygo
* @Date 2020/8/10 13:28
* @Version 1.0
*/
public class MetaspaceOOMTest {
static class OOMTest {
}
public static void main(String[] args) {
int i = 0;//模拟多少次后发生异常
try {
while (true) {
i++;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMTest.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor){
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return methodProxy.invokeSupper(o, args);
}
enhancer.create();
}
}
} catch (Throwable e) {
System.out.println("********多少次后发生了异常:" + i);
e.printStackTrace();
}
}
}
-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
GC垃圾回收算法和垃圾收集器的关系?分别是什么?
GC算法(引用计数/复制/标记清除/标记整理)是内存回收的方法论,垃圾收集器是算法的落地实现
目前为止还没有完美的收集器出现,更没有万能的收集器,只有针对具体应用最合适的收集器,进行分代收集
四种主要的垃圾收集器:串行、并行、并发、G1
串行垃圾回收器(Serial)
它为单线程环境设计,且只使用一个线程进行垃圾回收,会暂停所有的用户线程,所以不适合服务器环境
程序 --> GC --> 程序 --> GC --> 程序 --> GC
并行垃圾回收器(Parallel)
多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算/大数据处理首台处理等弱交互场景
--> GC --> GC --> GC
程序 --> GC --> 程序 --> GC --> 程序 --> GC
--> GC --> GC --> GC
并发垃圾回收器(Concurrent Mark Sweep)
用户线程和垃圾收集线程同时执行(不一定并行,可能交替执行)不需要停顿用户线程。互联网公司多用,适用对响应时间有要求的场景
--> GC --> 程序 --> 程序
程序 --> GC --> 程序 --> GC --> 程序 --> GC
--> GC --> GC --> 程序
G1 垃圾回收器(Garbage first)
G1垃圾回收器将堆内存分割成不同的区域然后并发地对其进行垃圾回收
串行与并行、STW与并发
怎么看服务器默认的垃圾收集器是哪个?生产上如何配置垃圾收集器? 谈谈你对垃圾收集器的理解
命令行指令:java -XX:+PrintCommandLineFlags -version
C:\Users\Heygo\Desktop\Interview>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=266620736 -XX:MaxHeapSize=4265931776 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocatio
n -XX:+UseParallelGC
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
bool Arguments::check_gc_consistency() {
bool status = true;
// Ensure that the user has not selected conflicting sets
// of collectors. [Note: this check is merely a user convenience;
// collectors over-ride each other so that only a non-conflicting
// set is selected; however what the user gets is not what they
// may have expected from the combination they asked for. It's
// better to reduce user confusion by not allowing them to
// select conflicting combinations.
uint i = 0;
if (UseSerialGC) i++;
if (UseConcMarkSweepGC || UseParNewGC) i++;
if (UseParallelGC || UseParallelOldGC) i++;
if (UseG1GC) i++;
if (i > 1) {
jio_fprintf(defaultStream::error_stream(),
"Conflicting collector combinations in option list; "
"please refer to the release notes for the combinations "
"allowed\n");
status = false;
}
return status;
}
垃圾收集器概述
垃圾收集器就来具体实现这些GC算法并实现内存回收。不同厂商、不同版本的虚拟机实现差别很大,HotSpot中包含的收集器如下图所示:
垃圾收集器对应的组合关系
串行GC(Serial)/(Serial Copying)
串行收集器:Serial收集器
参数配置:
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:UseSerialGC
并行GC(ParNew)
ParNew(并行)收集器
参数配置:
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
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC
并行回收GC(Parallel)/(Parallel Scavenge)
Parallel 并行回收
参数配置:
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
或者
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldlGC
串行GC(Serial Old)/(Serial MSC)
参数配置:
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldlGC
并行GC(Parallel Old)/(Parallel MSC)
参数配置:
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldlGC
或者
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
并发标记清除GC(CMS)
参数配置:
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC
CMS 的 4 步过程
CMS 四步骤总结
优点
并发收集停顿时间低
缺点
-XX:CMSFulIGCsBeforeCompaction
(默认0,即每次都进行内存整理)来指定多少次CMS收集之后,进行一次压缩的Full GC。Demo 代码
/*
1.-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC
2.-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC
3.-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
4.-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC
5.-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC
6.-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
7.-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldGC (已经没有了)
*/
public class GCDemo {
public static void main(String[] args) {
System.out.println("GCDemo...");
try {
String str = "bjtu";
while (true) {
str += str + new Random().nextInt(77777777) + new Random().nextInt(88888888);
str.intern();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
以前收集器特点
年轻代和老年代是各自独立且连续的内存块
年轻代收集,使用单eden+S0+S1进行复制算法
老年代收集必须扫描整个老年代区域
都是以尽可能快速地执行GC为设计原则
G1(Garbage-First)收集器,是一款面向服务端应用的收集器
从官网的描述中,我们知道G1是一种服务器端的垃圾收集器,应用在多处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能的满足垃圾收集暂停时间的要求。另外,它还具有以下特性:
G1收集器的设计目标是取代CMS收集器,它同CMS相比,在以下方面表现的更出色:
为什么会出现 G1?
Region区域化垃圾收集器:最大的好处是化整为零,避免全内存扫描,只需要按区域来进行扫描即可
G1算法将堆划分为若干个区域(Region),它仍然属于分代收集器
在G1中,还有一种特殊的区域,叫Humongous(巨大的)区域
G1 回收步骤
G1收集器下的Young GC针对Eden区进行收集,Eden区耗尽后会被触发,主要是小区域收集+形成连续的内存块,避免内存碎片
G1 工作的四大步骤
G1 Demo 代码
/**
* @ClassName GCDemo
* @Description TODO
* @Author Heygo
* @Date 2020/8/10 18:40
* @Version 1.0
*/
public class GCDemo {
public static void main(String[] args) {
System.out.println("GCDemo...");
try {
String str = "bjtu";
while (true) {
str += str + new Random().nextInt(77777777) + new Random().nextInt(88888888);
str.intern();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
[GC pause (G1 Humongous Allocation) (young) (initial-mark) (to-space exhausted), 0.0032657 secs]
[Parallel Time: 1.8 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 116.9, Avg: 117.0, Max: 117.2, Diff: 0.2]
[Ext Root Scanning (ms): Min: 0.1, Avg: 0.3, Max: 0.6, Diff: 0.5, Sum: 2.4]
[Update RS (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8]
[Processed Buffers: Min: 0, Avg: 1.9, Max: 4, Diff: 4, Sum: 15]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 1.0, Avg: 1.2, Max: 1.3, Diff: 0.3, Sum: 9.8]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.4]
[Termination Attempts: Min: 1, Avg: 6.9, Max: 14, Diff: 13, Sum: 55]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.2]
[GC Worker Total (ms): Min: 1.5, Avg: 1.7, Max: 1.8, Diff: 0.2, Sum: 13.5]
[GC Worker End (ms): Min: 118.7, Avg: 118.7, Max: 118.7, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.2 ms]
[Other: 1.3 ms]
[Evacuation Failure: 0.9 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.3 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 5120.0K(6144.0K)->0.0B(1024.0K) Survivors: 0.0B->1024.0K Heap: 7351.7K(10.0M)->6167.8K(10.0M)]
[Times: user=0.09 sys=0.00, real=0.00 secs]
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0009353 secs]
[GC concurrent-mark-start]
[GC pause (G1 Humongous Allocation) (young) (to-space exhausted), 0.0037669 secs]
[Parallel Time: 2.7 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 122.1, Avg: 122.1, Max: 122.2, Diff: 0.1]
[Ext Root Scanning (ms): Min: 0.1, Avg: 0.4, Max: 2.6, Diff: 2.5, Sum: 3.3]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.1]
[Processed Buffers: Min: 0, Avg: 0.4, Max: 3, Diff: 3, Sum: 3]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.2, Diff: 0.2, Sum: 0.2]
[Object Copy (ms): Min: 0.0, Avg: 1.1, Max: 1.5, Diff: 1.5, Sum: 9.0]
[Termination (ms): Min: 0.0, Avg: 1.0, Max: 1.3, Diff: 1.3, Sum: 7.9]
[Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 8]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[GC Worker Total (ms): Min: 2.5, Avg: 2.6, Max: 2.6, Diff: 0.1, Sum: 20.6]
[GC Worker End (ms): Min: 124.7, Avg: 124.7, Max: 124.7, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 0.9 ms]
[Evacuation Failure: 0.5 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.2 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 1024.0K(1024.0K)->0.0B(1024.0K) Survivors: 1024.0K->0.0B Heap: 7372.3K(10.0M)->6188.4K(10.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[GC concurrent-mark-end, 0.0043902 secs]
[GC remark [Finalize Marking, 0.0001364 secs] [GC ref-proc, 0.0002039 secs] [Unloading, 0.0004323 secs], 0.0008988 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[GC cleanup 7372K->7372K(10M), 0.0005463 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
garbage-first heap total 10240K, used 4199K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000)
region size 1024K, 1 young (1024K), 0 survivors (0K)
Metaspace used 3510K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 384K, capacity 388K, committed 512K, reserved 1048576K
常用配置参数(了解)
G1 常用配置:
开发人员仅仅需要声明以下参数即可:
三步归纳:开始G1+设置最大内存+设置最大停顿时间
-XX:MaxGCPauseMilis=n:最大GC停顿时间,单位毫秒,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=100
G1 相较于 CMS 的优势
比起CMS有两个优势:
SpringBoot 微服务的生产部署和调参优化步骤
java -server -Xms1024m -Xmx1024m -XX+UseG1GC -jar springboot2019-1.0-SNAPSHOT.war
整机:top(查看 CPU、内存使用率等,也可使用精简版系统性能命令:uptime)
CPU:vmstat(查看 CPU 使用情况)
vmstat -n 2 3
参数含义
其他的查看指令
内存:free(查看应用程序可用内存数)
经验值
pidstat -p 进程号 -r 采样间隔秒数,可查看进程占用内存的情况
硬盘:df(查看磁盘剩余空间数)
磁盘io:iostat(磁盘IO性能评估)
磁盘块设备分布
pidstat -p 进程号 -r 采样间隔秒数,可查看进程磁盘的使用情况
网络io:ifstat(查看网络占用情况)
默认本地没有,下载ifstat
wget http://gael.roualland.free.fr/ifstat/ifstat-1.1.tar.gz
tar xzvf ifstat-1.1.tar.gz
cd ifstat-1.1
./configure
make
make install
查看网络 I/O 情况
假如生产环境出现CPU占用过高,请谈谈你的分析思路和定位
下面开始举例啦~~~
先用top命令找出cpu占比最高的,记下PID
ps -ef或者jps进一步定位,得知是一个怎么样的一个后台程序
定位到具体的线程或代码
ps -mp 进程编号 -o THREAD,tid,time
将需要的线程ID转换为16进制格式(英文小写格式)
printf “%x\n”:打印输出有问题的线程ID(用计算器算也可以)
jstack 进程ID | grep tid(16进制线程ID小写英文)-A60
-A60 表示打印出前 60 行
对于JDK自带的JVM监控和性能分析工具用过哪些?一般怎么使用
下面开讲
自带的JVM监控和性能分析工具是什么
自带的JVM监控和性能分析工具怎么用?
jps(虚拟机进程状况工具)
jinfo(java配置信息工具)
jmap(内存映像工具)
jstat(统计信息监视工具)
jstack(堆栈异常跟踪工具)
jvisualvm
jconsole