[JVM]Java内存区域&&JVM性能调优(一)

JVM性能调优

在几次面试中,被问到JVM性能调优。由于项目经验少且没有接触到大型项目,对JVM性能调优的认知几乎为0,面试之前背了这方面的知识点,但记忆得不深刻,面试时还是哑口无言。
最近看了个关于JVM性能调优视频,感觉讲得挺好的,下面结合视频以及一些资料,总结一下JVM性能调优的知识。
视频地址

Java内存区域

JVM性能优化主要是体现在调整Java内存区域的一些重要参数。比如,JVM对整个堆内存进行full gc时,会STW(Stop the World,只运行垃圾回收线程,而暂停其他Java线程),STW过程会造成系统“卡顿”,用户体验感降低。因此,需要降低full gc的频率,full gc的触发条件是老年代堆内存放满,而老年代堆内存又与年轻代堆内存有密切关系。如果年轻代堆内存过小,老年代堆内存就会很快放满,full gc就会不断被触发,造成系统卡顿。如果年轻代堆内存过大,会造成一次垃圾收集(minor gc)的时间过长,也会造成系统卡顿。只有根据实际情况,调控堆内存空间各区域的比例,才能优化系统。

优化的JVM的前提是知道Java内存区域的构成。
Java内存区域构成

大致描述一下,.java文件经过编译器编译成.class文件,.class文件进入JVM被类加载子系统进行加工,为字节码执行作准备,最后由字节码执行引擎输出该.class对应的机器码文件,机器码文件交由操作系统执行。

上述这些内容想必大家也见不少,背过也忘过。个人认为,这些模型必须通过实际案例,将各区域的分工合作关系理清,才能够真正地理解和记住。

下面给我一段源码以及.class的反汇编结果:

public class JvmTest1 {

    public static void main(String[] args) {
        Animal a = new Animal("狗","汪汪汪");
        Animal b = new Animal("猫","喵喵喵");
    }
}

class Animal{
    private String name;
    private String call;

    public Animal(String name, String call) {
        this.name = name;
        this.call = call;
    }
}

对.class进行反汇编,并打印到JvmTest1文件:
给出部分反汇编内容:

Compiled from "JvmTest1.java"
   Code:
      0: new           #2                  // class Animal
      3: dup
      4: ldc           #3                  // String 狗
      6: ldc           #4                  // String 汪汪汪
      8: invokespecial #5                  // Method Animal."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     11: astore_1
     12: new           #2                  // class Animal
     15: dup
     16: ldc           #6                  // String 猫
     18: ldc           #7                  // String 喵喵喵
     20: invokespecial #5                  // Method Animal."<init>":(Ljava/lang/String;Ljava/lang/String;)V
     23: astore_2

下面主要看看JvmTest1.class的反汇编结果,我将结合Java运行时数据区各模块的协作来分析:
虚拟机栈是线程私有的(每个线程都有自己的虚拟机栈),当main线程运行时,main方法就压入main线程的虚拟机栈了,虚拟机栈的构成如下图所示,
在这里插入图片描述
可以看到,虚拟机栈的组成单位是栈帧,而每个栈帧由4部分组成:

  • 局部变量表:存储该方法的局部变量
  • 操作数栈:存放数值
  • 动态链接:
  • 方法出口:函数结束后,返回到调用该方法的地方

Animal a = new Animal("狗","汪汪汪");创建Animal对象,Java内存区域都有哪些变化呢?根据观察容易发现,这段代码对应反汇编的:

	   0: new           #2                  // class Animal
       3: dup
       4: ldc           #3                  // String 狗
       6: ldc           #4                  // String 汪汪汪
       8: invokespecial #5                  // Method Animal."<init>":(Ljava/lang/String;Ljava/lang/String;)V
      11: astore_1

Java创建一个对象的过程比较复杂,这里简单说说,执行new指令时Jvm会去常量池中定位一个类的符号引用,并且检查符号引用代表的类是否已经被加载、解析和初始化过,如果没有则需要进过类加载过程;如果有,则在堆空间中挖出一块空间给该对象,并且赋零值和设置对象头信息,最后执行invokespecial指令,调用该类的构造函数,按照程序员的意愿对对象进行初始化。
以上是《深入理解Java虚拟机》一书中提到的,但根据反汇编结果来看,new与invokespecial之间还经过了dup和两次ldc,为什么呢?
查阅资料发现,new指令做了两件事:创建对象和并将该对象的引用地址压入操作数栈。而dup指令做的事是:复制操作数栈栈顶元素的值,并将其压入栈顶。这句话有点绕,简单地说,执行完dup之后,操作数栈顶有两个连续相同的两个对象的引用地址,对象的引用地址就是常听到的this。而两次ldc的作用是:获取常量池中的“狗”和“汪汪汪”,将它们压入操作数栈栈顶。此时操作数栈顶的前3元素依次是:“汪汪汪”、“狗”、this。
在这里插入图片描述
这三个元素都是为了invokespecial准备的。现在栈顶元素是剩下的this。
最后执行astore_1指令将this存到局部变量表中的a变量中,然后this出栈。
在这里插入图片描述
以上就是创建一个Animal对象的大致过程

未完待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值