JVM 探究 (下)

GC 详解:

  口诀:关于垃圾回收:分代收集算法 ——不同的区域使用不同的算法

  Young代:GC频繁区域

  Old代:GC次数较少

  Perm代:不会产生GC

  一个对象的历程!

 

 

JVM在进行GC时,并非每次都是对三个区域进行扫描,大部分时候都是指的新生代!

  两个类型:

    普通GC:只针对新生代 -- GC

    全局GC:主要是针对老年代,偶尔伴随新生代 -- Full GC

 

GC算法

  1. 复制算法
  2. 标记清除
  3. 标记整理
  4. GC引用计数 (了解,基本不用)

  GC引用计数(了解)

 

 

  闲谈:GC可达性算法

 

 

  复制算法

    年轻代中使用的就是复制算法

  优点:没有标记和清除的过程,效率高,没有内存碎片

  缺点:需要浪费双倍的内存空间

 

  Eden区,对象存活率极低,据官方统计,99%的对象在使用一次后,引用失效,推荐使用复制算法

 

 

  标记清除算法

    老年代一般使用这个,但是会和标记整理算法一起使用

  优点:不需要额外空间

  缺点:两次扫描耗时,会产生内存碎片,不连续

 

 

  标记清除整理算法

    减少了标记清楚算法的缺点:没有内存碎片,但耗时较为严重

  那什么时候考虑使用这个算法?

    在我们这个要使用的算法的空间中,假设这个空间中很少,不经常发生GC,那么可以考虑使用这个算法!

 

GC算法总结

  内存效率:复制算法 > 标记清楚算法 > 标记清楚整理算法 (时间复杂度)

  内存整齐度:复制算法 = 标记清除整理算法 > 标记清除算法(内存碎片)

  内存利用率:标记清除整理算法 = 标记清除算法 > 复制算法

 

从效率来说,复制算法最好,但是空间浪费较多!为了兼顾所有指标,标记清除整理会平滑一点,但是效率不尽人意!

 

难道就没有一种最优的算法吗?

  答案:没有!分代收集算法:不同的区域使用不同的算法!没有最好的,只有最适合的。

 

JVM参数

  JVM三种参数类型:

    标配参数、X参数(参数)、XX参数;

  标配参数:

  • -version
  • -help
  • -showversion

  X参数(了解):

  • -Xint            #解释执行
  • -Xcomp       #第一次使用就编译成 本地 的代码
  • -Xmixed      #混合模式(java默认)

  (XX参数之布尔型)重点:jinfo -flag   

  jps -l                           #查看当前运行的Java线程号

  jinfo -flag PrintGCDetails 11111 #查看此线程号的Java程序的某个参数是否开启

  • +PrintGCDetails     #开启打印GC日志 “ +/- ”加减号分别表示开启关闭

  XX参数之 key = value型:

  • -XX:MetaspaceSize=128m           #元空间大小
  • -XX:MaxTenuringThresho=15       #进入老年区的存货年限

  ------------------------------------------------------

  • -XX:PrintFlagInital(了解)  #查看Java环境初始默认值,“ = ”表示默认值,“ := ”表示修改过

  经典面试题:-Xms、-Xmx、怎么解释呢?考察你到底研究过没有!

  1. -Xms 初始堆的大小,等价:-XX:InitialHeapSize

  2. -Xmx 最大堆的大小,等价:-XX:MaxHeapSize

 

你常用的项目,发布后配置过JVM调优参数吗?

  • -Xms
  • -Xmx
  • -Xss
  • -Xmn
  • -XX:MetaspsaceSize             #设置元空间的大小,这个在本地内存中
  • -XX:+PrintGCDetails              #打印GC
  • -XX:SurvivoRatio                   #设置新生代中s0/s1空间的比例
    • uintx SurvivorRatio = 8    Eden : s0 : s1 = 8 : 1 : 1
  • -XX:NewRatio                        #设置年轻代与老年代的占比
    • NewRatio = 2   新生代 :老年代 = 1 : 2  默认新生代是整个堆的1/3
  • MaxTenyringThreshold = 15  #进入老年区的存活阈值

 

关于对 OOM 的认识

 

 1 // -Xms8m -Xmx8m -XX:+PrintGCDetails
 2 // java.lang.StackOverflowError 堆栈溢出错误
 3 public class OOM_Demo1 {
 4     public static void main(String[] args) {
 5         a();
 6     }
 7     public static void a(){
 8         a();
 9     }
10 }

 

 1 // -Xms8m -Xmx8m -XX:+PrintGCDetails
 2 // java.lang.OutOfMemoryError: Java heap space java堆空间错误
 3 public class OOM_Demo2 {
 4     public static void main(String[] args) {
 5         String str = "hello";
 6         while (true) {
 7             str += str +
 8                     new Random(999999999) +
 9                     new Random(999999999);
10         }
11     }
12 }

 

 1 // -Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
 2 // java.lang.OutOfMemoryError: GC overhead limit exceeded
 3 // GC回收时间过长也会导致OOM
 4 // CPU占用一直100%,GC但是没有什么效果!
 5 public class OOM_Demo3 {
 6     public static void main(String[] args) throws Throwable {
 7         int i = 0;
 8         List<String> list = new ArrayList<String>();
 9 
10         try {
11             while (true){
12                 list.add(String.valueOf(++i).intern());
13             }
14         } catch (Throwable e) {
15             System.out.println("i=>"+i);
16             e.printStackTrace();
17             throw e;
18         }
19     }
20 }

 

 1 // -Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
 2 // java.lang.OutOfMemoryError: Direct buffer memory
 3 // 基础缓冲区的错误
 4 public class OOM_Demo4 {
 5     public static void main(String[] args) throws InterruptedException {
 6         System.out.println("配置的MaxDirectMemorySize"+
 7                 VM.maxDirectMemory()/1024/(double)1024+"MB");
 8         TimeUnit.SECONDS.sleep(2L);
 9 
10         // 故意破环
11         // ByteBuffer.allocate(); 分配JVM的堆内存,属于GC管辖
12         // ByteBuffer.allocateDirect(); 分配本地OS内存,不属于GC管辖
13         ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6*1024*1024);
14     }
15 }

用虚拟机执行,慎用自己电脑执行——会死机

 1 // java.lang.OutOfMemoryError: unable to create  native Thread
 2 // 高并发 ,  unable to create  native Thread这个错误更多的时候和平台有关!
 3 // 1、应用创建的线程太多!
 4 // 2、服务器不允许你创建这么多线程!
 5 public class TDemo {
 6     public static void main(String[] args) {
 7         for (int i = 1; ; i++) {
 8             System.out.println("i=>"+i);
 9             new Thread(()->{
10                 try {
11                     Thread.sleep(Integer.MAX_VALUE);
12                 } catch (InterruptedException e) {
13                     e.printStackTrace();
14                 }
15             },""+i).start();
16         }
17     }
18 }

 

 1 // -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
 2 // java.lang.OutOfMemoryError: Metaspace 元空间报错
 3 /*
 4 java8 之后使用元空间代替永久代;本地内存!
 5 1、虚拟机加载类信息
 6 2、常量池
 7 3、静态变量
 8 4、编译后的代码
 9 ..... 
10 模拟元空间溢出、不断的生成类即可!
11 */
12 public class OomDemo {
13     
14     static class OOMTest{}
15 
16     public static void main(String[] args) throws Throwable {
17 
18         int i = 0; // 模拟计数器
19 
20         try {
21             while (true){
22                 i++;
23                 // 不断的加载对象! Spring的 cglib;
24                 Enhancer enhancer = new Enhancer();
25                 enhancer.setSuperclass(OOMTest.class);
26                 enhancer.setUseCache(false);
27                 enhancer.setCallback(new MethodInterceptor() {
28                     @Override
29                     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
30                         return method.invoke(o,args);
31                     }
32                 });
33                 enhancer.create();
34             }
35         } catch (Throwable e) {
36             System.out.println("i=>"+i);
37             e.printStackTrace();
38         }
39         
40     }
41 }

 

垃圾收集器(难点!了解即可)

  4种垃圾回收器:

  1. 串行(STW:Stop the World)单线程
    1. STW:意思是执行GC的时候所有线程会停止运行,等待GC完成
  2. 并行(多线程工作,也会导致STW)
  3. 并发(在回收的同时,可以正常执行线程,并行处理,但是如果是单核CPU,只能交替执行!)
  4. G1(将堆内存分割成不同的区域,然后并发的对其进行垃圾回收)

java -XX:+PrintCommandLineFlags -version  #看默认的垃圾回收器

 

 

 

Java主要的GC回收器有哪些?(曾经7种,现在6种)

 

 

  •  DefNew:默认的新一代(Serial串行)
  • Tenured:老年代(Serial Old)
  • ParNew:并行新一代(并行ParNew)
  • PSYoungGen:并行清除年轻代(Parallel Scavcegn)
  • ParOldGen:并行老年区

 不同的GC回收器的机制不同,使用标准,如何选择:

  • -XX:UseSerialGC                    #单CPU,单机程序,内存小
  • -XX:+UseParallelGC    #多CPU,大吞吐量,后台计算
  • -XX:+UseParNewGC              #多CPU,但是不希望有时间停顿,快速响应   

  垃圾回收器设计原则:尽量少而快的执行GC!

G1回收器

  -XX:+UseG1GC          #使用G1GC 

  -XX:MaxGCPauseMillis=100    #最大的GC停顿时间单位:毫秒,JVM尽可能的保证停顿小于这个时间

  优点:

    1. 没有内存碎片!
    2. 可以精准控制垃圾回收时间  

 

posted @ 2020-03-13 01:30  执笔人生  阅读(258)  评论(0编辑  收藏  举报