深入浅出了解JVM

深入理解JVM
本文详细介绍了JVM的基本结构,包括PC寄存器、方法区、java堆、java栈的作用及特性,并阐述了java线程的工作内存与主内存之间的交互过程。此外还提供了JVM的常用配置参数以及类装载流程。

JVM的基本结构

了解JVM是每个学java人的必经之路,JVM对于java就像心脏对于人一样,JVM的结构如下图:
JVM的基本结构
1. PC寄存器
每个线程拥有一个PC寄存器,在线程创建时创建,指向的是下一条指令地址,但是在执行本地方法时,PC的值是未定义的。
2. 方法区
保存装载的类的元信息(类型的常量池,字段、方法信息,方法字节码等),被所以的线程共享
3. java堆
和方法区一样,所以的线程都共享java 堆,对分代GC算法来说,堆也是分代的,结构如下:
java堆
4. java栈
java栈为线程私有,由一系列帧组成,帧中保存一个方法的局部变量、操作数栈、常量池指针
注意:小对象在没有逃逸的情况下,可以直接分配在栈上,如果要取消优化可以使用-XX:-DoEscapeAnalysis参数
5. java线程
每一个线程有一个私有工作内存(java栈、本地栈、PC寄存器),这个工作内存与主内存是独立的,并且工作内存存放主存中变量值的拷贝,工作内存和主存之间可以用下图表示:
thread内存模型
当数据从主存复制到工作内存时,必须经过两个动作:read由主存执行读操作,load由工作内存执行load操作;当数据从工作内存拷贝到主存时,也必须经过两个动作:store由工作内存执行store操作,write主存相应的write操作。
因此对于普通变量,一个线程更新的值,不能马上反应在主存中,可以使用volatile关键字来消除这种情况,但是volatile并不能保证100%的同步,因为指令重排会破坏线程间的有序性,如下面这个例子:

class Test
{
    volatile int a=0;
    boolean flag=false;
    //线程1调用
    public void writer()
    {
        a=1;
        flag=true;
        /*上面两条语句在编译时为了优化,可能发生指令重排,重排后的结果如下
        *flag=true;
        *a=1;
        */
    }
    //线程2调用
    public void reader()
    {
        if(flag)
        {
            int i=a+1;
            ......
        }
    }
}

JVM的配置参数

  1. -verbose:[gc|class|jni]
    打开GC日志,显示已加载类的信息,显示有关JNI调用的信息
  2. -XX:+PrintGC、-XX:+PrintGCDetails、-XX:+PrintGCTimeStamps
    显示GC的信息
  3. -Xloggc:path
    指定GC log的位置,以文件输出
  4. -XX:PrintHeapAtGC
    每次GC后,都打印堆信息
  5. -XX:+TraceClassLoading
    监控类的加载,与-verbose:class相同
  6. -XX:+PrintClassHistogram
    打印类的信息
  7. -Xmx -Xms
    指定最大堆和最小堆
  8. -Xmn -XX:NewRatio -XX:SurvivorRatio
    设置新生代的大小、新生代与老年代的比值、老年代与幸存器的比值
  9. -XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath -XX:OnOutOfMemoryError
    OOM时导出堆到文件及文件的路径、执行的脚本
  10. -Xss
    分配栈的大小

类装载器

class装载流程

  1. 加载
    装载类的第一个阶段,取得类的二进制流,转为方法区数据结构,在java堆中生成对应的java.lang.Class对象
  2. 链接
    1. 验证
      验证的目的是保证Class流的格式是正确的,包括文件格式验证(是否以0xCAFEBABE开头、版本号是否合理),元数据验证(是否有父类、是否继承了final类等),字节码验证,符号引用验证
    2. 准备
      分配内存,并为类设置初始值(方法区中)
      注意 final static变量在这阶段被直接赋值
    3. 解析
      将符号引用替换为直接引用
  3. 初始化
    1. 执行类的构造器clinit(包括static变量赋值语句、static 语句块)
    2. 子类的clinit调用前保证父类的clinit被调用
    3. clinit是线程安全的

JDK中的ClassLoader

classLoader

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值