码迷,mamicode.com
首页 > 编程语言 > 详细

[Java] 开课吧--JVM

时间:2021-06-02 12:28:20      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:关心   简单   转化   不一致   基于   根据   java数据类型   roots   绑定   

双亲委派

  • 向上委托,向下加载
  • 收到加载任务后,先交给父类加载器,只有当父类加载器无法完成,才会执行加载
  • 保证只有一个类加载器加载,避免重复加载
  • 破坏:JDK 1.2后才使用,JDK  1.1的核心类没有通过双亲委派定义

技术图片

技术图片

如何判断两个Class对象是否相同

  • class字节码相同
  • classLoader相同

JVM运行数据区按线程使用情况分类

  • 线程独享区域
    • 虚拟机栈、本地方法栈、程序计数器
    • 不需要垃圾回收
  • 线程共享区域
    • 堆和方法区
    • 垃圾回收、类的静态数据和对象

技术图片

HotSpot

  • JDK 1.6:字符串常量池在永久代
  • JDK 1.7:依然有永久代,但字符串常量池在堆中
  • JDK 1.8:不存在永久代

字符串常量池

  • 节省内存空间,所有类共享一个字符串常量池
  • 如何存数据
    • 对象存储
    • 快速搜索:StringTable(hashtable)
      • StringTableSize:决定搜索效率

技术图片

  • JVM中最大的一块内存
  • GC最重要区域
  • 堆的内存分配通过垃圾收集器实现
  • JVM启动时实现
  • 存什么
    • JDK 1.6:对象、数组
    • JDK 1.7+:对象、数组、字符串常量池、静态变量 
  • 参数
    • -Xms:初始大小,单位m,g
    • -Xmx:最大大小
    • -XMn:新生代(年轻代)
    • -XX:SurvivorRatio=8:年轻代中Eden与Servivor比例,默认为8:1
    • -XX:PermSize:非堆内存初始大小,一般默认200m
    • -XX:MaxPermSize:菲堆最大大小
    • -XX:NewSize(-Xns):年轻代内存初始大小
    • -XX:MaxNewSize(-Xmn):年轻代最大大小   

技术图片

 技术图片

  • 分配原则
    • 优先在Eden分配,如果Eden不足,进行一次MinorGC
    • 大对象直接进入老年代(超过Eden一半)
    • 长期存活对象进入老年代(15次GC未被回收)
    • 年轻代没有空间存放的对象进入老年代
  • 分配方式
    • 指针碰撞:内存地址连续(年轻代)
    • 空闲列表:内存地址不连续(老年代)
  • 分配安全
    • 虚拟机给A线程分配内存过程中,指针未修改,B线程可能同时使用了同一块内存
    • CAS(乐观锁):不加锁,假设没有冲突,如果冲突就重试,直到成功
    • TLAB(本地线程分配缓冲):每个线程预先分配一块内存
  • 分配担保
    • 当在新生代中无法分配内存时,把新生代对象转移到老年代,然后把新对象放入腾空的新生代
  • 对象内存布局
    • 对象头:锁信息、对象年龄、类型指针
    • 实例数据:成员变量
    • 对齐填充

程序计数器

  • 当前线程执行下一个字节码的行号
  • 用于线程切换后,能恢复到正确执行的位置

Java虚拟机栈

  • 基于栈和基于寄存器
    • 基于栈(操作数栈):一个程序调用需要10个基于栈的指令,JVM一次编译,到处运行
    • 基于寄存器:同样的一个程序调用只需要两三个基于寄存器的调用,和操作系统有关
  • 栈帧
    • 支持虚拟机进行方法执行的数据结构
    • 存储方法的局部变量表、操作数栈、动态链接、方法返回地址
    • 局部变量表--冰箱;操作栈--盘子
    • 方法调用时创建
    • 操作数栈的每个元素可以是任意Java数据类型,32位数据类型占一个栈容量
    • 每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,可在类加载阶段或第一次使用时转换为直接引用(静态解析),或每次运行时转换为直接引用(动态连接)
    • 静态解析:直接引用,即对应方法的内存地址
    • 动态链接:class文件中,一个方法调用其他方法,需要将方法的符号引用转化为内存地址的直接引用。类中调用父类方法,运行时执行子类方法(多态)
    • 方法返回:把当前栈帧出栈。正常完成出口 / 异常完成出口

技术图片

本地方法栈

  • 直接操作硬件,C++方法

技术图片

方法执行

  • 先由Java编译器编译为Java字节码,再由Java解释器逐条解释,或对热点代码由JIT编译器编译运行
  • 热点代码:被多次调用的方法;被多次执行的循环体
  • 方法调用计数器:先检查是否存在已编译版本,否则方法调用次数加1,超过阈值则启动编译
  • 加载存储指令
    • 将一个局部变量表加载到操作数栈:load系列
    • 将一个数值从操作数栈加载到局部变量表:store系列
    • 将一个常量加载到操作数栈:const系列、push系列、ldc系列

技术图片

  •  JIT运行方式
    • Server模式,Client模式
    • 优化
      • 公共子表达式消除
      • 方法内联
      • 逃逸分析(存在逃逸则无法优化)
        • 栈上内存分配
        • 标量替换
        • 同步锁消除

方法调用

  • 常见方法调用类型
    • 私有方法:与类绑定,编译时确定
    • 构造方法:与类绑定,编译时确定
    • 静态方法:与类绑定,编译时确定
    • 成员方法:不与类绑定,运行时确定
    • 接口方法:不与类绑定,运行时确定
    • 编译看左边,运行看右边
  • 重载与重写
    • 重写(overwrite):也叫方法覆盖,继承或实现关系下,子类和父类方法描述符(参数和返回值)一致,方法名称一致
    • 重载(overloading):在同一个类中,方法名称一致,方法参数(类型和顺序)不一致,不关心返回值
      • 可理解为运行时重新加载,即编译时按左边类,运行时才加载右边真正类型
  • 静态绑定和动态绑定
    • 通过类名、方法名、方法描述符识别方法
    • 属性看左,方法看右
    • 通过父类引用访问子类的属性,需要强制转型
  • 方法调用指令
    • invokevirtual:调用非静态非私有方法(多态)
    • invokeinterface:调用接口方法(多态)
    • invokespecial:调用非静态私有方法、构造方法
    • invokestatic:调用静态方法
    • invokedynamic
  • 方法调用过程
    • 静态绑定方法:直接在运行时常量池找到引用
    • 动态绑定方法:根据父类方法表确定要查找的方法索引(编译看左),从子类方法表中开始,找不到再去父方法表找
  • 虚分配
    • Father father = new Son();
    • father指针指向son对象
    • 根据本地变量表,找调用该虚方法的对象
    • 取出对象头中的类型指针,找到Class对象
    • 找到Class对象后,找到对应的虚方法表
    • 找到对应的方法,进行方法调用
    • 如果找不到,则去父类对象查找,最终找不到则报错
public class DynamicCall01 {
	public static void main(String[] args) {
		Father father = new Son();
		// 多态,发生方法重载
		father.f1(); 
		// 打印结果: Son-f1()
		char c = ‘a‘;
		father.f1(c);
		// 打印结果: father-f1() para-int 97
	}
}

// 被调用的父类
class Father {
	public void f1() {
		System.out.println("father-f1()");
	}

	public void f1(int i) {
		System.out.println("father-f1() para-int " + i);
	}
}

// 被调用的子类
class Son extends Father {
	public void f1() {
		// 覆盖父类的方法
		System.out.println("Son-f1()");
	}
	public void f1(char c) {
		System.out.println("Son-s1() para-char " + c);
	}
}
    • 例题
      • 对于father.f1(),子类父类都有,在各自方法表中是独立的两项(编号相同),编译期间无法确定,故在运行时根据创建的对象,发生重载
      • 对于father.f1(c),f(char)父类没有,编译时将char自动转型为int,而f(int)只有父类有,子类直接继承,也不会写在子类方法表中,故不会发生重载。如果Son中也有f1(int i),则会发生重载
      • 子类对象通过父类引用调用的方法,必须在父类中出现过,否则编译无法通过
  •  内联缓存
    • 加快动态绑定的优化技术

技术图片

垃圾回收

  • 标记阶段
    • 引用计数:实现简单,无法处理循环引用问题。Python采用
    • 可达性分析:枚举GC Roots-->STW-->追踪标记。Java采用
    • 回收过程
      • 第一次判断:使用可达性分析后,判断对象不可达
      • 第二次判断:finalize(),重新建立可达性关联,否则会被第二次标记
  • 垃圾清除阶段
    • 标记-清除算法:效率不高,GC时需停止程序,清理后内存不连续
    • 复制算法:没有标记和清除过程,效率高,空间连续,需要两倍空间(空间换时间),适用于新生代(存活对象少,垃圾对象多)
    • 标记-压缩算法:适用于老年代(存活对象多)
  • 垃圾回收器
    • 按执行方式分类
      • 串行回收器:Serial、Serial Old
      • 并行回收器:ParNew、Parallel Scavenge、Parallel Old
      • 并发回收器:CMS、G1
    • 按垃圾分代关系
      • 新生代收集器:Serial、ParNew、Parallel Scavenge
      • 老年代收集器:Serial Old、Parallel Old、CMS
      • 整堆收集器:G1
    • 性能指标
      • 吞吐量:运行用户代码时间/(运行用户代码时间+垃圾收集时间)【高吞吐,后台】
      • 暂停时间:应用程序线程暂停,GC线程执行的时间段【低延迟,页面】
    • CMS
      • 低延迟
      • 标记-清除算法
      • 不能和PS配合
      • 初始标记->并发标记->重新标记->并发清除

 

[Java] 开课吧--JVM

标签:关心   简单   转化   不一致   基于   根据   java数据类型   roots   绑定   

原文地址:https://www.cnblogs.com/cxc1357/p/14193475.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!