深入理解java虚拟机课程的截图-2-宋红康老师

【来自B站视频:https://www.bilibili.com/video/BV1BJ41177cp?p=66

【JVM学习路径:上面这张图就是的】


 

JVM虚拟机规范各种参数官方手册:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BGBCIEFC

【初始堆大小和最大堆大小建议设置成一样的:防止堆内存扩容和缩容带来的额外的系统开销。】

  

package com.atguigu.java;
import java.util.ArrayList;
import java.util.Random;
//-Xms600m -Xmx600m
public class OOMTest {
    public static void main(String[] args) {
        ArrayList<Picture> list = new ArrayList<>();
        while(true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add(new Picture(new Random().nextInt(1024 * 1024)));
        }
    }
}
class Picture{
    private byte[] pixels;
    public Picture(int length) {
        this.pixels = new byte[length];
    }
}

【注意:默认情况下,JVM中 年轻代与老年代大小比例是 1 : 2,一般情况下,不会修改这个比例。

但是如果,声名周期很长的对象有很多,可以适当增大老年代空间大小。】

【注意:查看JVM中的各个参数的值。jinfo -flag NewRatio 6988、jinfo -flag SurvivorRatio 6988】

 

     

【每次执行完GC之后,谁空谁是SurvivorTo区,与之对应的是SurvivorFrom区。绝大部分的对象都是朝生夕死,90%以上。】

下面的三张图详细的描述了对象的分配过程,请细看,要会描述:

【年龄计数器:每个对象都有一个年龄计数器,这个年龄计数器代表着该对象从年轻代晋升到老年代所需要的年龄值。如果这个年龄计数器大于年龄阈值就需要晋升了。这个年龄阈值默认是15,可以通过JVM参数设置(-XX:MaxTenuringThreshold=15)。当Eden区满的时候,会触发YGC;但是当S0区或S1区满的时候,不会触发YGC。Eden区是主动GC,S0与S1区是被动GC。】

    

  

【记住下面这张图!!!】

package com.atguigu.java1;
import java.util.ArrayList;
import java.util.List;
/**
 * 测试MinorGC 、 MajorGC、FullGC
 * -Xms9m -Xmx9m -XX:+PrintGCDetails
 */
public class GCTest {
    public static void main(String[] args) {
        int i = 0;
        try {
            List<String> list = new ArrayList<>();
            String a = "atguigu.com";
            while (true) {
                list.add(a);
                a = a + a;
                i++;
            }
        } catch (Throwable t) {
            t.printStackTrace();
            System.out.println("遍历次数为:" + i);
        }
    }
}

【堆空间一定都是共享的么?不一定,比如TLAB,它就是每个线程私有的一小块区域,是JVM分配内存的首选。】

package com.atguigu.java1;
//测试 -XX:UseTLAB 参数是否开启的情况:默认情况是开启的
public class TLABArgsTest {
    public static void main(String[] args) {
        System.out.println("我只是来打个酱油~");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

JVM参数官网(600多个):https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BGBCIEFC

JVM参数官网:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BGBCIEFC

【如果新生代空间设置过小,那么好多对象还没来及被回收就晋升到老年代去了,导致YoungGC意义就不大了。】

空间分配担保 参数的解释如下:(JDK7之后不再受人为设置影响了)

 

package com.atguigu.java2;
/**
 * 栈上分配测试
 * -Xmx1G -Xms1G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails
 * 默认情况下是开启逃逸分析的:-Xmx1G -Xms1G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails
 */
public class StackAllocation {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 10000000; i++) {
            alloc();
        }
        // 查看执行时间
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为: " + (end - start) + " ms");
        // 为了方便查看堆内存中对象个数,线程sleep
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
    private static void alloc() {
        User user = new User();//未发生逃逸
    }
    static class User { }
}

同步省略测试:

标量替换测试:

【-XX:+UseTLAB -XX :+EliminateAllocations -XX:+DoEscapeAnalysis -XX:+PrintGCDetails】

package com.atguigu.java2;
/**
 * 标量替换测试:标量替换功能默认是开启的
 *  -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations
 *  -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
 */
public class ScalarReplace {
    public static class User {
        public int id;
        public String name;
    }
    public static void alloc() {
        User u = new User();//未发生逃逸
        u.id = 5;
        u.name = "www.atguigu.com";
    }
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为: " + (end - start) + " ms");
    }
}
/*
class Customer{
    String name; //标量
    int id; //标量
    Account acct; //聚合量
}
class Account{
    double balance;
}
 */

 

【标量替换已经应用了,但是栈上分配并没有在JVM中应用】

【注意1:虚拟机栈和本地方法栈,会出现异常,但不会出现GC。程序计数器,不会出现异常,也不会出现GC。】

【注意2:堆和元空间,会出现异常,也会出现GC。】

JVM中方法区描述https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4

【随着java版本的升级,内存结构基本上变化很小了,但是GC是有些变化的。】

【类的元数据(也就是类的结构信息),即 Class文件是存储在方法区中的。】

【方法区类似于接口,不同的JVM对它有不同的实现。HotSpot JVM 实现时,把方法区和堆区分开,并给它一个别名,叫非堆。同时,不同的java版本,对它也有不同的落地实现,JDK7及之前,落地实现叫永久代;JDK8及之后,落地实现叫元空间。】

JVM参数列表:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BGBCIEFC

 

 

反编译命令:【javap -v JVM.class】【javap -v -p JVM.class】【javap -v -p JVM.class > test.txt】
package com.atguigu.java;
import java.io.Serializable;
//测试方法区的内部构成
public class MethodInnerStrucTest extends Object implements Comparable<String>,Serializable {
    //属性
    public int num = 10;
    private static String str = "测试方法的内部结构";
    //构造器
    //方法
    public void test1(){
        int count = 20;
        System.out.println("count = " + count);
    }
    public static int test2(int cal){
        int result = 0;
        try {
            int value = 30;
            result = value / cal;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    @Override
    public int compareTo(String o) {
        return 0;
    }
}

 

 

package com.atguigu.java1;
public class MethodAreaDemo {
    public static void main(String[] args) {
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}

【字节码反编译:javap -v -p MethodAreaDemo.class】    反编译之后,结果如下:

Classfile /D:/workspace_idea5/JVMDemo/out/production/chapter09/com/atguigu/java1/MethodAreaDemo.class
  Last modified 2020-4-23; size 640 bytes
  MD5 checksum a2e8a0e034e2dd986b45d36a3401a63b
  Compiled from "MethodAreaDemo.java"
public class com.atguigu.java1.MethodAreaDemo
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#24         // java/lang/Object."<init>":()V
   #2 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #27.#28        // java/io/PrintStream.println:(I)V
   #4 = Class              #29            // com/atguigu/java1/MethodAreaDemo
   #5 = Class              #30            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/atguigu/java1/MethodAreaDemo;
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               args
  #16 = Utf8               [Ljava/lang/String;
  #17 = Utf8               x
  #18 = Utf8               I
  #19 = Utf8               y
  #20 = Utf8               a
  #21 = Utf8               b
  #22 = Utf8               SourceFile
  #23 = Utf8               MethodAreaDemo.java
  #24 = NameAndType        #6:#7          // "<init>":()V
  #25 = Class              #31            // java/lang/System
  #26 = NameAndType        #32:#33        // out:Ljava/io/PrintStream;
  #27 = Class              #34            // java/io/PrintStream
  #28 = NameAndType        #35:#36        // println:(I)V
  #29 = Utf8               com/atguigu/java1/MethodAreaDemo
  #30 = Utf8               java/lang/Object
  #31 = Utf8               java/lang/System
  #32 = Utf8               out
  #33 = Utf8               Ljava/io/PrintStream;
  #34 = Utf8               java/io/PrintStream
  #35 = Utf8               println
  #36 = Utf8               (I)V
{
  public com.atguigu.java1.MethodAreaDemo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/atguigu/java1/MethodAreaDemo;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: sipush        500
         3: istore_1
         4: bipush        100
         6: istore_2
         7: iload_1
         8: iload_2
         9: idiv
        10: istore_3
        11: bipush        50
        13: istore        4
        15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        18: iload_3
        19: iload         4
        21: iadd
        22: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        25: return
      LineNumberTable:
        line 9: 0
        line 10: 4
        line 11: 7
        line 12: 11
        line 13: 15
        line 14: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  args   [Ljava/lang/String;
            4      22     1     x   I
            7      19     2     y   I
           11      15     3     a   I
           15      11     4     b   I
}
SourceFile: "MethodAreaDemo.java"

 

【istore_3 指令将 5 押入操作数栈】

 

 

【站点:http://openjdk.java.net/jeps/122】不断的去问什么?才能不断加深自身技术!

【前面说了,在JDK6以及之前,都是使用永久代来存储类元信息/静态变量/字符串常量池等,在JDK7的时候就有了去永久代的想法,但是直到JDK8的时候才真正去除,并用元空间来代替永久代的功能。要注意,JDK7的时候有了这个想法,它不能什么也不做啊,所以它也做了一些动作,比如,把静态变量和字符串常量池从永久代移到了堆空间,并一直坚持下去,直到JDK8以及之后,都保存在堆空间。那么,为什么要把静态变量(名)和字符串常量池移到堆空间呢?请看下面的截图解释。【StringTable:就是字符串常量池、或者叫做字符串字面量】

arr这个是对象引用,JDK6及之前存储在永久代,JDK7及之后存储在堆空间。

new byte[1024*1024*100] ,这个对象不管是JDK几都是存储在堆空间。

JVM规范:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

 

【对象的内存布局=对象头+实例数据+对齐填充】

一个对象内存布局的举例如下:

    

   

【Java虚拟机规范中并没有说要使用哪一种对象访问方式。但是,HotSpot虚拟机使用的是直接指针方式!优点:节省空间(不用在堆空间单独开辟一块内存去维护句柄)、速度快(直接通过局部变量表中对象引用来获取对象数据,不用句柄做中间媒介了)。缺点:GC的时候需要移动对象,一旦对象发生了移动,它的内存地址就发生变化了,所以对象的对象头里面维护的对象地址也需要跟着改变(也即对象头不稳定)。另外:句柄池方式的话,对象的对象头是稳定的,因为它只需要修改句柄池就行了,局部变量表中对象对应的句柄引用是不用修改的。】

JDK8及其之后,元空间使用的就是直接内存(也叫做本地内存,直接向系统申请的内存)。

 

【IO:阻塞式传输。NIO:非阻塞式传输。】

【一开始分配的是1G本地内存,之后就释放了。】

【非直接缓冲区的方式:存在同一份数据从用户态向内核态拷贝的过程,导致数据存储了两份,浪费空间,效率低。】

这里可以学习下nettyhttps://www.bilibili.com/video/BV1DJ411m7NR?from=search&seid=11346077094614353796

 

【方法区(元空间),也存在异常和GC,这个GC是指FullGC过程,不过回收垃圾的时候过程很艰辛。】

【OOM:Heap Space。OOM:Meta Space。OOM:Direct Buffer Memory。OOM:Perm Space。】

 

执行引擎是非常重要的,下面开始你的表演吧。。。

【前端编译(编译java代码生成字节码)+后端编译(执行引擎中的JIT即时编译器做的热点代码编译)】

同声传译,牛!

 

 

【 从程序源码到抽象语法树,是编译javac的过程。java是半解释型半编译型语言:半解释型,就从程序源码解释执行这一个过程。半编译型,就是从程序源码目标代码这一过程。橙色部分和JVM无关,绿色和蓝色是JVM要考虑的事情。】

【java语言,有两种代码执行方式,一种是解释型执行过程,一种是JIT编译型执行过程,两者是相互结合起来的。java的执行速度发展到今天,可以说是已经不慢了,可以和C/C++媲美了。】

 

【机器码 <—— 机器指令(指令集) <—— 汇编语言 <—— 高级语言】

 

【机器码 <—— 机器指令(指令集) <—— 汇编语言 <—— 高级语言】

【字节码文件,是一种中间状态。执行引擎,专门翻译字节码文件为机器指令,交给CPU执行。】

【C、C++:编译过程 ——> 汇编过程】

【 从程序源码到抽象语法树,是编译javac的过程。java是半解释型半编译型语言:半解释型,就从程序源码解释执行这一个过程。半编译型,就是从程序源码目标代码这一过程。橙色部分和JVM无关,绿色和蓝色是JVM要考虑的事情。橙色部分是前端编译器的过程,蓝色部分是JIT编译器的过程。】

         

 

【冷机状态:机器刚刚启动之后的状态。热机状态:机器已经启动并且稳定运行很长时间了。注意:机器在热机状态下可以承受的负载要高于冷机状态。如果以热机状态的流量进行切流,有可能会使处于冷机状态的服务器因无法一下子承载这么多的流量而假死。】

 【JVM监视命令:jconsole、jvisualvm、jps、jinfo、jstat】

【JIT编译器啥时候工作?见下图中的“如何选择”。热点代码探测,方法调用计数器,回边计数器。】

package com.atguigu.java;
//测试解释器模式和JIT编译模式
//-Xint:6520ms,纯解释器模式。-Xcomp:950ms,纯编译器模式。-Xmixed:936ms,解释器与编译器的混合模式
public class IntCompTest {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        testPrimeNumber(1000000);
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为:" + (end - start));
    }
    public static void testPrimeNumber(int count){
        for (int i = 0; i < count; i++) {
            //计算100以内的质数
            label:for(int j = 2;j <= 100;j++){
                for(int k = 2;k <= Math.sqrt(j);k++){
                    if(j % k == 0){
                        continue label;
                    }
                }
                //System.out.println(j);
            }
        }
    }
}

JVM参数(client/server):https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BGBCIEFC

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
视频目录 第1节说在前面的话 [免费观看] 00:05:07分钟 | 第2节整个部分要讲的内容说明 [免费观看] 00:06:58分钟 | 第3节环境搭建以及jdk,jre,jvm的关系 [免费观看] 00:20:48分钟 | 第4节jvm初体验-内存溢出问题的分析与解决 [免费观看] 00:17:59分钟 | 第5节jvm再体验-jvm可视化监控工具 [免费观看] 00:21:17分钟 | 第6节杂谈 [免费观看] 00:12:37分钟 | 第7节Java的发展历史00:27:24分钟 | 第8节Java的发展历史续00:02:27分钟 | 第9节Java技术体系00:08:46分钟 | 第10节jdk8的新特性00:07:31分钟 | 第11节lanmbda表达式简介00:07:02分钟 | 第12节Java虚拟机-classic vm00:06:06分钟 | 第13节Java虚拟机-ExactVM00:03:35分钟 | 第14节Java虚拟机-HotSpotVM00:04:23分钟 | 第15节Java虚拟机-kvm00:03:04分钟 | 第16节Java虚拟机-JRockit00:04:12分钟 | 第17节Java虚拟机-j900:04:23分钟 | 第18节Java虚拟机-dalvik00:02:20分钟 | 第19节Java虚拟机-MicrosoftJVM00:03:57分钟 | 第20节Java虚拟机-高性能Java虚拟机00:02:58分钟 | 第21节Java虚拟机-TaobaoVM00:03:06分钟 | 第22节Java内存区域-简介00:07:56分钟 | 第23节Java内存区域-Java虚拟机栈00:12:04分钟 | 第24节Java内存区域-程序计数器00:12:54分钟 | 第25节Java内存区域-本地方法栈00:02:39分钟 | 第26节Java内存区域-内存00:05:08分钟 | 第27节Java内存区域-方法区00:06:32分钟 | 第28节Java内存区域-直接内存和运行时常量池00:15:53分钟 | 第29节对象在内存中的布局-对象的创建00:21:19分钟 | 第30节探究对象的结构00:13:47分钟 | 第31节深入理解对象的访问定位00:08:01分钟 | 第32节垃圾回收-概述00:06:20分钟 | 第33节垃圾回收-判断对象是否存活算法-引用计数法详解00:14:08分钟 | 第34节垃圾回收-判断对象是否存活算法-可达性分析法详解00:07:09分钟 | 第35节垃圾回收算法-标记清除算法00:04:36分钟 | 第36节垃圾回收算法-复制算法00:14:35分钟 | 第37节垃圾回收算法-标记整理算法和分代收集算法00:05:24分钟 | 第38节垃圾收集器-serial收集器详解00:09:45分钟 | 第39节垃圾收集器-parnew收集器详解00:04:53分钟 | 第40节垃圾收集器-parallel收集器详解00:11:02分钟 | 第41节垃圾收集器-cms收集器详解00:14:58分钟 | 第42节最牛的垃圾收集器-g1收集器详解00:18:04分钟 | 第43节内存分配-概述00:04:23分钟 | 第44节内存分配-Eden区域00:22:51分钟 | 第45节内存分配-大对象直接进老年代00:06:42分钟 | 第46节内存分配-长期存活的对象进入老年代00:03:40分钟 | 第47节内存分配-空间分配担保00:04:54分钟 | 第48节内存分配-逃逸分析与栈上分配00:10:32分钟 | 第49节虚拟机工具介绍00:10:27分钟 | 第50节虚拟机工具-jps详解00:11:20分钟 | 第51节虚拟机工具-jstat详解00:09:20分钟 | 第52节虚拟机工具-jinfo详解00:05:03分钟 | 第53节虚拟机工具-jmap详解00:08:48分钟 | 第54节虚拟机工具-jhat详解00:08:10分钟 | 第55节虚拟机工具-jstack详解00:10:19分钟 | 第56节可视化虚拟机工具-Jconsole内存监控00:07:09分钟 | 第57节可视化虚拟机工具-Jconsole线程监控00:12:18分钟 | 第58节死锁原理以及可视化虚拟机工具-Jconsole线程死锁监控00:10:38分钟 | 第59节VisualVM使用详解00:08:03分钟 | 第60节性能调优概述00:11:22分钟 | 第61节性能调优-案例100:23:28分钟 | 第62节性能调优-案例200:10:05分钟 | 第63节性能调优-案例300:12:41分钟 | 第64节前半部分内容整体回顾00:15:41分钟 | 第65节Class文件简介和发展历史 [免费观看] 00:11:26分钟 | 第66节Class文件结构概述 [免费观看] 00:16:50分钟 | 第67节Class文件设计理念以及意义 [免费观看] 00:13:41分钟 | 第68节文件结构-魔数 [免费观看] 00:09:49分钟 | 第69节文件结构-常量池 [免费观看] 00:23:44分钟 | 第70节文件结构-访问标志 [免费观看] 00:11:36分钟 | 第71节文件结构-类索引00:11:26分钟 | 第72节文件结构-字段表集合00:13:21分钟 | 第73节文件结构-方法表集合00:10:06分钟 | 第74节文件结构-属性表集合00:18:23分钟 | 第75节字节码指令简介00:09:18分钟 | 第76节字节码与数据类型00:09:34分钟 | 第77节加载指令00:09:33分钟 | 第78节运算指令00:10:24分钟 | 第79节类型转换指令00:13:42分钟 | 第80节对象创建与访问指令00:09:38分钟 | 第81节操作树栈指令00:03:27分钟 | 第82节控制转移指令00:11:58分钟 | 第83节方法调用和返回指令00:06:37分钟 | 第84节异常处理指令00:09:44分钟 | 第85节同步指令00:07:34分钟 | 第86节类加载机制概述00:07:26分钟 | 第87节类加载时机00:13:15分钟 | 第88节类加载的过程-加载00:15:15分钟 | 第89节类加载的过程-验证00:10:24分钟 | 第90节类加载的过程-准备00:05:40分钟 | 第91节类加载的过程-解析00:14:04分钟 | 第92节类加载的过程-初始化00:19:41分钟 | 第93节类加载器00:22:41分钟 | 第94节双亲委派模型00:17:03分钟 | 第95节运行时栈帧结构00:08:46分钟 | 第96节局部变量表00:20:48分钟 | 第97节操作数栈00:08:36分钟 | 第98节动态连接00:02:56分钟 | 第99节方法返回地址和附加信息00:03:24分钟 | 第100节方法调用-解析调用00:09:49分钟 | 第101节方法调用-静态分派调用00:16:21分钟 | 第102节方法调用-动态分派调用00:09:02分钟 | 第103节动态类型语言支持00:09:27分钟 | 第104节字节码执行引擎小结00:03:38分钟 | 第105节总结与回顾00:10:55分钟
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值