JVM2.

JVM体系结构

JVM

双亲委派机制

  • 类代码加载,并实例化的过程:
    • class.car → class loader
    • 加载 + 初始化:class loader → class car模版
    • new :相当于从class car模版实例化一个对象
    • getClass:和new相反。getClassLoader可以获得加载器
  • 双亲委派机制:new 一个对象,寻找类的过程:
    • 现在boot加载器中寻找(jre/lib/rt.jar)
    • 再去ext加载器中寻找(jre/lib/ext/…)
    • 最后在app加载器(用户写的)寻找
  • 双亲委派的作用:
    • 防止用户自己写系统自带的类。例如自己写String是无效的,除非修改rt.jar,但是修改会导致所有的使用String的其他类崩溃
public class Car {
    public static void main(String[] args) {
        Car car1 = new Car();
        Car car2 = new Car();
        System.out.println(car1 == car2);       //false,故其hascode不同
        System.out.println(car1.getClass() == car2.getClass()); //true,故其hascode相同
        System.out.println(car1.getClass().getClassLoader());     //AppClassLoader
        System.out.println(car1.getClass().getClassLoader().getParent());   //ExtClassLoader       

    }
}

SandBox机制

  • 组成
    • 字节校验码:要求用户写的代码符合要求。但是核心类(java/…和javax/…)不需要检验
    • 类装载器(其实是双亲委派机制在起作用):不可改变核心类

Native

  • 代码:
public class Demo1 {
    public static void main(String[] args) {
        new Thread(()->{

        },"adair");
    }
    //native:java本身无法完成,无方法体,需要调用底层
    //native会进入本地方法栈 → JNI → 本地方法库
    //JNI作用,扩展java语言,融合不同编程语言为java使用。
    //例如java驱动打印机,管理系统,写外挂
    private native void start0();
}

内存

方法区

  • 方法区是一种规范,实现之前是永久区,如今是元空间。所有定义方法的信息都保存在该区域。
  • 静态变量、常量、类信息(构造方法/定义接口)、运行时的常量池都在方法区(static、final、class、 常量池)。但是对象存在于堆中

  • main最先执行,最后结束
  • 线程结束,栈内存就释放。对于栈不存在垃圾
  • 栈内:基本类型 + 对象引用 + 实例方法
  • 栈内以栈帧为单位
  • 程序正在运行的方法,一定在栈的顶部

  • JVM有三种hotspotJ9VMJRockit

  • 一个JVM只有一个堆,且堆内存的大小可变

  • 堆的东西:类、方法、常量、变量、 真实对象

  • 堆的三个区域:

    • 新生区:伊甸园eden、幸存去s0(from)、s1(to)。轻GC
    • 养老区:常用的数据。重GC(Full GC)
    • 永久区(元空间)
  • 新生区:类的诞生 + 甚至死亡的地方。

  • eden:对象被new的地方,满了就会触发轻GC,幸存者存放在s1

  • 如果幸存区满了,引发重GC(全局清理),送入养老区

  • 如果养老区满了,则OOM

  • 永久区:常驻内存,存放JDK自身的Class对象。interface元数据,存储的是java运行时的环境。这个区域无GC,关闭JVM即可释放此区域。一个启动类,加载大量第三方jar包,或者生成大量的反射类,直到OOM

    • jdk1.6 :永久代,常量池在方法区
    • jdk1.7:永久代(退化),常量池在堆中
    • jdk1.8:无永久带,常量池在元空间(不再JVM中,本地内存)中
  • 元空间、永久区、方法区、常量池关系:java8 hotspot取消了永久区。方法区是一个规范,规范没变,它就一直在,取而代之的是元空间,元空间存储类的元信息,静态变量和常量池等并入堆中。

  • 元空间:逻辑存在,物理不存在

  • JVM调参:

public class Car {
    public static void main(String[] args) {
        //jvm试图使用的最大内存
        long l = Runtime.getRuntime().maxMemory();
        //jvm初始化的内存
        long l1 = Runtime.getRuntime().totalMemory();
        //默认最大为电脑内存的1/4,初始化为1/64
        System.out.println(l/(double)1024/1024);
        System.out.println(l1/(double)1024/1024);

        //jvm调参:-Xms1024m -Xmx2g -XX:+PrintGCDetails
    }
}

Jprofiler

在一个很大项目中,出现了OOM,OOM要想捕获用Error e捕获,如何使用专业工具排错

  • Jprofiler作用
    • 分析dump文件,定位泄露
    • 获得堆的数据
  • 安装过程:
    • 现在idea中找好Jprofiler接口
    • 然后下载Jprofiler,记住安装路径(environment)
    • 然后配置接口到jprofiler9/bin/jprofiler.exe
  • Vm options配置-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
  • 点击运行代码(死循环),即可生成带有OOM的dump文件,使用jprofiler打开即可分析
  • dump文件分析:
    • 可以查看最大的数据在哪里,很大程度OOM
    • Thread Dump可查看线程的main函数中具体第几行出现错误

GC

JVM的GC几乎全部在堆中,对堆的清理大部分在eden区

  • 轻GC:在新生区进行操作,操作后会将eden活的对象移到幸存区,从而eden为空。当一个对象经过默认值15次还没死,就去养老区
  • Full GC:全局
  • GC算法:
    • 引用计数法:顾名思义。计数器本身也会有消耗,辣鸡
    • 复制算法:s0 s1相互转换,没有内存碎片,但是永远有一半未使用
    • 标记清除算法:先进行标记,再清除。两次扫描浪费时间,有内存碎片。
    • 标记清除压缩

总结

内存效率:复制算法 > 标记清除 > 标记压缩 (时间复杂度)

内存整齐度:复制算法 = 标记压缩 > 标记清除

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

  • JVM调优:没有最好的算法,只有最合适的算法 --> 分代收集算法
    • 年轻代:存活低、故使用复制算法
    • 老年代:区域大,存活高。使用标记清楚 + 标记压缩 实现

JMM

  • JMM就是java memory module,相当于是一个规则
  • 作用:
    • 缓存一致性协议,用于定义数据读写的规则
    • JMM定义了线程和内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory),每个线程都有一个私有的本地内存(local memory),线程的本地内存是从主内存复制的,由于多个线程对一个对象进行操作,因此需要解决volatile
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值