zoukankan      html  css  js  c++  java
  • JVM内存模型

    引自:https://www.bilibili.com/video/BV1e5411N7Jm?p=1

    https://segmentfault.com/a/1190000023867772

    一、Java程序的运行

    Java跨平台

    Java中的类先编译成class字节码文件,然后JVM加载字节码文件运行得到结果。

    通过JVM来从软件层面屏蔽不同的操作系统在底层硬件与指令上的区别。

    JVM就是Java跨平台的根本。

    二、一个Java程序运行的过程

    public class Main {
        public static void main(String[] args) {
            System.out.println(add());
        }
    
        public static int add(){
            int a=1;
            int b=2;
            int c=(a+b)*100;
            return c;
        }
    }
    

    对class文件反汇编可以得到字节码文件,使用命令javap -c Main.class

    Compiled from "Main.java"
    public class com.yqd.Main {
      public com.yqd.Main();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: invokestatic  #3                  // Method add:()I
           6: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
           9: return
    
      public static int add();
        Code:
           0: iconst_1
           1: istore_0
           2: iconst_2
           3: istore_1
           4: iload_0
           5: iload_1
           6: iadd
           7: bipush        100
           9: imul
          10: istore_2
          11: iload_2
          12: ireturn
    }
    
    栈帧的局部变量其实是用一个数组进行存储的
    其中特殊的局部变量0就是this
    public int compute();
    Code:
    0: iconst_1 将局部变量1 放入到操作数栈
    1: istore_1 将int类型的值存入局部变量1(将int值赋给在局部变量表的局部变量)
    2: iconst_2
    3: istore_2
    (这就明白了 这两个实际上指行的就是 现在局部变量表中开辟一个b的空间 然后在从操作数栈中弹栈赋给局部变量b)
    4: iload_1 //局部变量1压入栈
    5: iload_2 //局部变量2 压入栈
    6: iadd //弹栈两次 执行int 类型的add
    7: iconst_3 //将计算结果 压入栈
    8:bipush //将 10压入栈
    9: imul //计算乘法结果 在压入栈中
    10: istore_3 //将结果存给局部变量3
    11: iload_3 //取出局部变量3的值
    12: ireturn //return int
    

    三、Java程序执行:

    1. 就是将class文件加载进类加载子系统
    2. 开辟一个包含 堆,方法区,栈,本地方法栈,程序计数器的空间
    3. 字节码引擎开始执行

    四、JVM模型

    1.程序计数器

    • 当前线程所执行的字节码行号指示器;
    • 分支、循环、跳转等控制;
    • 当执行的是java方法时是正在执行的虚拟机字节码指令的地址;
    • 当执行的是Native(JNI)方法时该指针为空;
    • 没有(out of memory error)OOM。

    2.JVM栈帧

    • 生命周期与线程相同;
    • 每个方法执行时都会创建一个栈帧(stack frame);
    • 用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
    • 每个方法从调用到执行成的过程对应栈帧在栈中入栈到出栈的过程;
    • 栈深过大会StackOverFlowError;内存不足会OOM;

    栈帧存的内容(4种):

    1. 局部变量表 : 存放着方法中的局部变量
    2. 操作数栈: 用来操作方法中的数的一个临时栈
    3. 动态链接:把符号引用存在直接应用存在内存空间中
    4. 方法出口: 记录该方法调用完毕应该回到的地方 (放到我们这个例子中就是回到Main函数的下一行)

    3.本地方法栈

    Native method stack,跟栈一样,栈服务于java方法,本地方法栈服务于native 方法(JNI),部分虚拟机是合并的。

    4.堆

    • 所有线程共享;

    • 存放对象实例;

    • 垃圾回收的主要区域;

    • 物理内存上可以不连续;

    • 会有OOM问题;

    • 局部变量表会指向

    • 方法区 中的静态变量也会指向

    5.方法区(JDK1.8改为元空间)

    • 线程共享;
    • 用于存储已经被虚拟机加载的类信息,常量、静态变量等;
    • 类的加载、卸载、常量池回收均发生在此;
    • 内存不足会有OOM ;
    • 运行时常量池:方法区一部分,存放编译时期生成的各种字面量和符号引用,具有动态特性,内存不足也会OOM;**运行时常量池时放在方法区

    直接内存

    • 与JVM定义内存区域无关,不归JVM管理;
    • 内存不足也会OOM;
    • native库直接分配的堆外内存;不会回收。例如Netty缓冲区,不需要回收,可以反复用。

    元空间

    方法区又称永久代在1.8称为元空间。

    元空间替换永久代原因

    ​ 1、Java7及以前版本的Hotspot中方法区位于永久代中。同时,永久代和堆是相互隔离的,但它们使用的物理内存是连续的。永久代的垃圾收集是和老年代捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的垃圾收集。

    ​ 2、元空间存在于本地内存,意味着只要本地内存足够,它不会出现像永久代中“java.lang.OutOfMemoryError: PermGen space”这种错误。看上图中的方法区,是不是“膨胀”了。默认情况下元空间是可以无限使用本地内存的,但为了不让它如此膨胀,JVM同样提供了参数来限制它使用的使用。

    ​ 表面上看是为了避免OOM异常。因为通常使用PermSize和MaxPermSize设置永久代的大小就决定了永久代的上限,但是不是总能知道应该设置为多大合适, 如果使用默认值很容易遇到OOM错误。当使用元空间时,可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制。

    ​ 更深层的原因还是要合并HotSpot和JRockit的代码,JRockit从来没有所谓的永久代,也不需要开发运维人员设置永久代的大小,但是运行良好。同时也不用担心运行性能问题了,在覆盖到的测试中, 程序启动和运行速度降低不超过1%,但是这点性能损失换来了更大的安全保障。

    总结:

    • 永久代:GC不会再程序运行期间对永久代进行垃圾回收,这会导致OOM。
    • 元数据空间:不存在虚拟机中,而是使用本地内存,大小由系统实际可用内存控制。
    元空间配置参数
    -XX:MetaspaceSize,class metadata的初始空间配额,以bytes为单位,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当的降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize(如果设置了的话),适当的提高该值。
    -XX:MaxMetaspaceSize,可以为metadata分配的最大空间,默认是没有限制的。
    -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比。
    -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比。
    

    常量池

    常量池

    ​ Class可以理解为Class文件的资源仓库.class文件中除了包含类的版本、字段、方法、接口等描述信息,还有一项就是常量池,常量池中用于存放编译期间生成的各种字面变量和符号引用。

    ​ 八中基本类型中byte、short、integer、long、char等在值小于等于127使用对象池,即不负责创建和管理大于127的对象。

    字符串常量池
    1. 字符串的分配和其他的对象分配一样,耗费高昂的时间和空间代价,作为基础数据,大量创建字符串影响程序性能。
    2. JVM为了提高性能和减少开销,在实例化字符串常量时进行了优化。
    • 为字符串开辟一个字符串常量池,类似缓存区。
    • 创建字符串常量是,先检查字符串常量池是否存在该字符串。
    • 存在则返回该字符串的引用,不存在时则实例化该字符串并放入池中(String s=new String("abc");这种方式会新建出字符串,与常量池的不同)。

    例子:

    String s1 = "abc"; String s2 = "abc"; String s3 = new String("abc"); String s4 = new String("abc"); s1==s2    s2!=s3    s3!=s4
    

    例题1:String str=new String("abc");创建了多少个对象?

    答:创建过程如下:

    1. 在常量池查找"abc"对象,有则返回对应的引用实例,没有则在常量池创建对应实例对象。
    2. 在堆中new一个String("abc")对象。
    3. 将对象地址赋值给str,创建一个引用。

    因此,常量池没有"abc"字面量则创建两个对象,否则创建一个对象以及创建一个对象的引用。

    例题2:String str=new String("a"+"b");创建了多少个对象?

    答:字符串常量池:a、b、ab。

    ​ 堆:new String("ab")。

    ​ 引用:str。

    合计5个。

    TLAB

    ​ TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区。

    ​ JVM分配对象是优先分配到线程栈上,栈上分配不了的(如对象较大)则直接分配在Old区;如果对象不大,优先分配在栈上的TLAB上。

    ​ TLAB是在Eden区的专门的内存空间,为了防止在Eden区分配空间的多线程竞争资源,JVM为每个线程在Eden区上分配的专属内存空间即TLAB。

    五、内存分配和对象布局篇

    1、JOL对象内存布局

    Java Object Layout 对象的内存布局:即对象在内存中如何分布的。

    image

    数组对象:markword(8) + classPointer(4) + 数组长度(4) + 实例数据 + 对齐

    Ps:压缩指针和压缩普通对象指针:

    使用java -XX:+PrintCommandLineFlags -version命令可以看到包含以下信息:

    显示: -XX:+UseCompressedClassPointers -XX:+UseCompressedOops,

    其中-XX:+UseCompressedClassPointers是使用压缩类指针,原先是8字节,由于8字节=8x8=64位,264位寻址空间太大,因此没必要使用8字节,因此使用了压缩成4字节的压缩指针,4字节的寻址能力:4*8=32位,232=4G(2^10=1024=1KB 2^20=1M 2^30=1G) 又由于JVM是8字节一寻址,也就是每8字节作为一个单位,因此实际寻址能力4G*8=32G,ZGC号称最大能够使用4T的内存,ZGC使用8字节作为ClassPointer 其中有42位为类指针,4位为颜色指针,2^42=4T。

    其中-XX:+UseCompressedOops是表示使用压缩对象指针进行寻址,两个指令应该是一对的。

    关闭压缩指针-XX:-UseCompressedClassPointers -XX:-UseCompressedOops指令。

    2、为新对象分配内存的方式

    • 内存规整:直接移动指针到未被使用的区域(指针碰撞),需要带压缩的GC:Serial、ParNew等带compact的垃圾回收器。
    • 内存不规整:空闲列表维护可用空间,例如:CMS等基于mark-sweep的垃圾回收器。

    3、内存分配的线程安全

    ​ CAS保证,每个线程在jvm预先分配了内存,称为本地线程分配缓冲区TLAB,并同步锁定。

    4、对象的访问定位

    ​ 栈上的reference数据操作具体堆上的对象有句柄和直接指针两种方式。

    句柄方式:jvm堆上划分内存来作为句柄池,引用时引用存储对象的句柄地址,句柄中包含对象实例数据和类型数据的各自具体地址信息。

    ​ 优点:引用不用修改,比较稳定,对象移动如垃圾回收只要改变句柄值即可。

    image

    直接指针:引用直接引用对象地址,对象中包含类型数据指针,指向方法区class。

    ​ 优点:速度快。

    image

    5、对象的创建过程

    • new 指令,开辟空间(申请、初始化),这里的初始化是半初始化,还是默认值,例如对象中有变量 int i=8; 此时初始化后i=0 即默认值,所以称为半初始化。

    • invokespecial ,构造方法,赋值(真正的初始化,i=8)。

    • astore, 对象建立关联,栈空间与堆空间建立关联。

    • 初次访问对象

      其中2、3两部会发生指令重排。

    6、对象逃逸

    JVM的3中运行模式:

    解释模式:只使用解释器,执行一行JVM字节码就编译一行为机器码。这样能更快的看到程序的执行效果,但是执行的效果并不一定最快。适合执行一次的代码模块。

    编译模式:只使用编译器,先将所有的JVM字节码一次编译为机器码,然后一次性执行所有机器码。这样启动的稍慢,但执行完很快,适合反复执行的代码模块;

    混合模式:依然采用解释模式,但是对于一点热点代码采用编译模式,并把对应的字节码缓存起来。JVM一般采用混合模式。

    对象逃逸分析:

    public User t1(){
        User user = new User();
        user.setId(1);
        user.setName("sz");
        //写入数据库
        return user;
    }
    
    public User t2(){
        User user = new User();
        user.setId(1);
        user.setName("sz");
        //写入数据库
    }
    

    t1对象会被其他引用,作用范围不明显;t2对象不会被其他线程引用,直接分配到当前线程。

    对象逃逸分析的JVM参数: -XX:+DoEscapeAnalysis(开启)-XX:DoEscapeAnalysis(关闭)。

    JDK1.7之后默认开启,但如果栈空间不足会分配到堆空间。逃逸分析发生在编译期间。

    六、JVM指令集

    aconst_null 将null对象引用压入栈
    
    iconst_m1 将int类型常量-1压入栈
    
    iconst_0 将int类型常量0压入栈
    
    iconst_1 将int类型常量1压入栈
    
    iconst_2 将int类型常量2压入栈
    
    iconst_3 将int类型常量3压入栈
    
    iconst_4 将int类型常量4压入栈
    
    iconst_5 将int类型常量5压入栈
    
    lconst_0 将long类型常量0压入栈
    
    lconst_1 将long类型常量1压入栈
    
    fconst_0 将float类型常量0压入栈
    
    fconst_1 将float类型常量1压入栈
    
    dconst_0 将double类型常量0压入栈
    
    dconst_1 将double类型常量1压入栈
    
    bipush 将一个8位带符号整数压入栈
    
    sipush 将16位带符号整数压入栈
    
    ldc 把常量池中的项压入栈
    
    ldc_w 把常量池中的项压入栈(使用宽索引)
    
    ldc2_w 把常量池中long类型或者double类型的项压入栈(使用宽索引)
    
    从栈中的局部变量中装载值的指令
    
    iload 从局部变量中装载int类型值
    
    lload 从局部变量中装载long类型值
    
    fload 从局部变量中装载float类型值
    
    dload 从局部变量中装载double类型值
    
    aload 从局部变量中装载引用类型值(refernce)
    
    iload_0 从局部变量0中装载int类型值
    
    iload_1 从局部变量1中装载int类型值
    
    iload_2 从局部变量2中装载int类型值
    
    iload_3 从局部变量3中装载int类型值
    
    ireturn 从局部变量3中装载int类型值
    
    lload_0 从局部变量0中装载long类型值
    
    lload_1 从局部变量1中装载long类型值
    
    lload_2 从局部变量2中装载long类型值
    
    lload_3 从局部变量3中装载long类型值
    
    fload_0 从局部变量0中装载float类型值
    
    fload_1 从局部变量1中装载float类型值fload_2 从局部变量2中装载float类型值
    
    fload_3 从局部变量3中装载float类型值
    
    dload_0 从局部变量0中装载double类型值
    
    dload_1 从局部变量1中装载double类型值
    
    dload_2 从局部变量2中装载double类型值
    
    dload_3 从局部变量3中装载double类型值
    
    aload_0 从局部变量0中装载引用类型值
    
    aload_1 从局部变量1中装载引用类型值
    
    aload_2 从局部变量2中装载引用类型值
    
    aload_3 从局部变量3中装载引用类型值
    
    iaload 从数组中装载int类型值
    
    laload 从数组中装载long类型值
    
    faload 从数组中装载float类型值
    
    daload 从数组中装载double类型值
    
    aaload 从数组中装载引用类型值
    
    baload 从数组中装载byte类型或boolean类型值
    
    caload 从数组中装载char类型值
    
    saload 从数组中装载short类型值
    
    将栈中的值存入局部变量的指令
    
    istore 将int类型值存入局部变量
    
    lstore 将long类型值存入局部变量
    
    fstore 将float类型值存入局部变量
    
    dstore 将double类型值存入局部变量
    
    astore 将将引用类型或returnAddress类型值存入局部变量
    
    istore_0 将int类型值存入局部变量0
    
    istore_1 将int类型值存入局部变量1
    
    istore_2 将int类型值存入局部变量2
    
    istore_3 将int类型值存入局部变量3
    
    lstore_0 将long类型值存入局部变量0
    
    lstore_1 将long类型值存入局部变量1
    
    lstore_2 将long类型值存入局部变量2
    
    lstore_3 将long类型值存入局部变量3
    
    fstore_0 将float类型值存入局部变量0
    
    fstore_1 将float类型值存入局部变量1
    
    fstore_2 将float类型值存入局部变量2
    
    fstore_3 将float类型值存入局部变量3
    
    dstore_0 将double类型值存入局部变量0
    
    dstore_1 将double类型值存入局部变量1dstore_2 将double类型值存入局部变量2
    
    dstore_3 将double类型值存入局部变量3
    
    astore_0 将引用类型或returnAddress类型值存入局部变量0
    
    astore_1 将引用类型或returnAddress类型值存入局部变量1
    
    astore_2 将引用类型或returnAddress类型值存入局部变量2
    
    astore_3 将引用类型或returnAddress类型值存入局部变量3
    
    iastore 将int类型值存入数组中
    
    lastore 将long类型值存入数组中
    
    fastore 将float类型值存入数组中
    
    dastore 将double类型值存入数组中
    
    aastore 将引用类型值存入数组中
    
    bastore 将byte类型或者boolean类型值存入数组中
    
    castore 将char类型值存入数组中
    
    sastore 将short类型值存入数组中
    
    wide指令
    
    wide 使用附加字节扩展局部变量索引
    
    通用(无类型)栈操作
    
    nop 不做任何操作
    
    pop 弹出栈顶端一个字长的内容
    
    pop2 弹出栈顶端两个字长的内容
    
    dup 复制栈顶部一个字长内容
    
    dup_x1 复制栈顶部一个字长的内容,然后将复制内容及原来弹出的两个字长的内容压入
    
    栈
    
    dup_x2 复制栈顶部一个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入
    
    栈
    
    dup2 复制栈顶部两个字长内容
    
    dup2_x1 复制栈顶部两个字长的内容,然后将复制内容及原来弹出的三个字长的内容压入
    
    栈
    
    dup2_x2 复制栈顶部两个字长的内容,然后将复制内容及原来弹出的四个字长的内容压入
    
    栈
    
    swap 交换栈顶部两个字长内容
    
    类型转换
    
    i2l 把int类型的数据转化为long类型
    
    i2f 把int类型的数据转化为float类型
    
    i2d 把int类型的数据转化为double类型
    
    l2i 把long类型的数据转化为int类型
    
    l2f 把long类型的数据转化为float类型
    
    l2d 把long类型的数据转化为double类型f2i 把float类型的数据转化为int类型
    
    f2l 把float类型的数据转化为long类型
    
    f2d 把float类型的数据转化为double类型
    
    d2i 把double类型的数据转化为int类型
    
    d2l 把double类型的数据转化为long类型
    
    d2f 把double类型的数据转化为float类型
    
    i2b 把int类型的数据转化为byte类型
    
    i2c 把int类型的数据转化为char类型
    
    i2s 把int类型的数据转化为short类型
    
    整数运算
    
    iadd 执行int类型的加法
    
    ladd 执行long类型的加法
    
    isub 执行int类型的减法
    
    lsub 执行long类型的减法
    
    imul 执行int类型的乘法
    
    lmul 执行long类型的乘法
    
    idiv 执行int类型的除法
    
    ldiv 执行long类型的除法
    
    irem 计算int类型除法的余数
    
    lrem 计算long类型除法的余数
    
    ineg 对一个int类型值进行取反操作
    
    lneg 对一个long类型值进行取反操作
    
    iinc 把一个常量值加到一个int类型的局部变量上
    
    逻辑运算
    
    移位操作
    
    ishl 执行int类型的向左移位操作
    
    lshl 执行long类型的向左移位操作
    
    ishr 执行int类型的向右移位操作
    
    lshr 执行long类型的向右移位操作
    
    iushr 执行int类型的向右逻辑移位操作
    
    lushr 执行long类型的向右逻辑移位操作
    
    按位布尔运算
    
    iand 对int类型值进行“逻辑与”操作
    
    land 对long类型值进行“逻辑与”操作
    
    ior 对int类型值进行“逻辑或”操作
    
    lor 对long类型值进行“逻辑或”操作
    
    ixor 对int类型值进行“逻辑异或”操作
    
    lxor 对long类型值进行“逻辑异或”操作浮点运算
    
    fadd 执行float类型的加法
    
    dadd 执行double类型的加法
    
    fsub 执行float类型的减法
    
    dsub 执行double类型的减法
    
    fmul 执行float类型的乘法
    
    dmul 执行double类型的乘法
    
    fdiv 执行float类型的除法
    
    ddiv 执行double类型的除法
    
    frem 计算float类型除法的余数
    
    drem 计算double类型除法的余数
    
    fneg 将一个float类型的数值取反
    
    dneg 将一个double类型的数值取反
    
    对象和数组
    
    对象操作指令
    
    new 创建一个新对象
    
    checkcast 确定对象为所给定的类型
    
    getfield 从对象中获取字段
    
    putfield 设置对象中字段的值
    
    getstatic 从类中获取静态字段
    
    putstatic 设置类中静态字段的值
    
    instanceof 判断对象是否为给定的类型
    
    数组操作指令
    
    newarray 分配数据成员类型为基本上数据类型的新数组
    
    anewarray 分配数据成员类型为引用类型的新数组
    
    arraylength 获取数组长度
    
    multianewarray 分配新的多维数组
    
    控制流
    
    条件分支指令
    
    ifeq 如果等于0,则跳转
    
    ifne 如果不等于0,则跳转
    
    iflt 如果小于0,则跳转
    
    ifge 如果大于等于0,则跳转
    
    ifgt 如果大于0,则跳转
    
    ifle 如果小于等于0,则跳转
    
    if_icmpcq 如果两个int值相等,则跳转
    
    if_icmpne 如果两个int类型值不相等,则跳转
    
    if_icmplt 如果一个int类型值小于另外一个int类型值,则跳转if_icmpge 如果一个int类型值大于或者等于另外一个int类型值,则跳转
    
    if_icmpgt 如果一个int类型值大于另外一个int类型值,则跳转
    
    if_icmple 如果一个int类型值小于或者等于另外一个int类型值,则跳转
    
    ifnull 如果等于null,则跳转
    
    ifnonnull 如果不等于null,则跳转
    
    if_acmpeq 如果两个对象引用相等,则跳转
    
    if_acmpnc 如果两个对象引用不相等,则跳转
    
    比较指令
    
    lcmp 比较long类型值
    
    fcmpl 比较float类型值(当遇到NaN时,返回-1)
    
    fcmpg 比较float类型值(当遇到NaN时,返回1)
    
    dcmpl 比较double类型值(当遇到NaN时,返回-1)
    
    dcmpg 比较double类型值(当遇到NaN时,返回1)
    
    无条件转移指令
    
    goto 无条件跳转
    
    goto_w 无条件跳转(宽索引)
    
    表跳转指令
    
    tableswitch 通过索引访问跳转表,并跳转
    
    lookupswitch 通过键值匹配访问跳转表,并执行跳转操作
    
    异常
    
    athrow 抛出异常或错误
    
    finally子句
    
    jsr 跳转到子例程
    
    jsr_w 跳转到子例程(宽索引)
    
    rct 从子例程返回
    
    方法调用与返回
    
    方法调用指令
    
    invokcvirtual 运行时按照对象的类来调用实例方法
    
    invokespecial 根据编译时类型来调用实例方法
    
    invokestatic 调用类(静态)方法
    
    invokcinterface 调用接口方法
    
    方法返回指令
    
    ireturn 从方法中返回int类型的数据
    
    lreturn 从方法中返回long类型的数据
    
    freturn 从方法中返回float类型的数据
    
    dreturn 从方法中返回double类型的数据
    
    areturn 从方法中返回引用类型的数据
    
    return 从方法中返回,返回值为void线程同步
    
    montiorenter 进入并获取对象监视器
    
    monitorexit 释放并退出对象监视器
    
    JVM指令助记符
    
    变量到操作数栈:iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_
    
    操作数栈到变量:
    
    istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_
    
    常数到操作数栈:
    
    bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_
    
    加:iadd,ladd,fadd,dadd
    
    减:isub,lsub,fsub,dsub
    
    乘:imul,lmul,fmul,dmul
    
    除:idiv,ldiv,fdiv,ddiv
    
    余数:irem,lrem,frem,drem
    
    取负:ineg,lneg,fneg,dneg
    
    移位:ishl,lshr,iushr,lshl,lshr,lushr
    
    按位或:ior,lor
    
    按位与:iand,land
    
    按位异或:ixor,lxor
    
    类型转换:i2l,i2f,i2d,l2f,l2d,f2d(放宽数值转换)
    
    i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(缩窄数值转换)
    
    创建类实便:new
    
    创建新数组:newarray,anewarray,multianwarray
    
    访问类的域和类实例域:getfield,putfield,getstatic,putstatic
    
    把数据装载到操作数栈:baload,caload,saload,iaload,laload,faload,daload,aaload
    
    从操作数栈存存储到数组:
    
    bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore
    
    获取数组长度:arraylength
    
    检相类实例或数组属性:instanceof,checkcast
    
    操作数栈管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap
    
    有条件转移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene,
    
    if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl
    
    fcmpg,dcmpl,dcmpg
    
    复合条件转移:tableswitch,lookupswitch
    
    无条件转移:goto,goto_w,jsr,jsr_w,ret
    
    调度对象的实便方法:invokevirtual
    
    调用由接口实现的方法:invokeinterface
    
    调用需要特殊处理的实例方法:invokespecial调用命名类中的静态方法:invokestatic
    
    方法返回:ireturn,lreturn,freturn,dreturn,areturn,return
    
    异常:athrow
    
    finally关键字的实现使用:jsr,jsr_w,ret
    

    七、JVM性能调优

    1.性能优化的目标是什么?

    通过对系统业务的预估,让整个Java虚拟机参数的设置更合理,最终达到的效果是,让朝生
    夕死的对象在年轻代就被回收,不会进入老年代,从而避免full GC。

    2.为什么要做JVM性能调优?

    • 防止出现OOM;
    • 解决OOM;
    • 减少full GC出现的频率,避免STW。

    3.为什么回收垃圾时要设计STW?

    避免在进行GC的时候线程运行结束,导致原本不是垃圾的对象变为垃圾,需要频繁回收。

    4.亿级流量系统内存分析

  • 相关阅读:
    E寻宝(贪心)
    千万别点进来,点进来你就哭了(最短路,dijkstra)
    H小明买年糕(前缀和+二分)
    Charles破解
    Jmeter安装插件
    appium环境搭建
    SourceTree安装和教程
    Appium-desktopAppium-desktop 安装与入门使用
    appium终端安装
    Seleinum_CSS定位方式
  • 原文地址:https://www.cnblogs.com/smalldong/p/14461196.html
Copyright © 2011-2022 走看看