双亲委派

  • 向上委托,向下加载
  • 收到加载任务后,先交给父类加载器,只有当父类加载器无法完成,才会执行加载
  • 保证只有一个类加载器加载,避免重复加载
  • 破坏: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对象后,找到对应的虚方法表
    • 找到对应的方法,进行方法调用
    • 如果找不到,则去父类对象查找,最终找不到则报错
  1. public class DynamicCall01 {
  2. public static void main(String[] args) {
  3. Father father = new Son();
  4. // 多态,发生方法重载
  5. father.f1();
  6. // 打印结果: Son-f1()
  7. char c = 'a';
  8. father.f1(c);
  9. // 打印结果: father-f1() para-int 97
  10. }
  11. }
  12.  
  13. // 被调用的父类
  14. class Father {
  15. public void f1() {
  16. System.out.println("father-f1()");
  17. }
  18.  
  19. public void f1(int i) {
  20. System.out.println("father-f1() para-int " + i);
  21. }
  22. }
  23.  
  24. // 被调用的子类
  25. class Son extends Father {
  26. public void f1() {
  27. // 覆盖父类的方法
  28. System.out.println("Son-f1()");
  29. }
  30. public void f1(char c) {
  31. System.out.println("Son-s1() para-char " + c);
  32. }
  33. }
    • 例题

      • 对于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的更多相关文章

  1. (转)Java 详解 JVM 工作原理和流程

    作为一名Java使用者,掌握JVM的体系结构也是必须的.说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Java编程语言.Java类文件格式.Java虚 ...

  2. 关于编写Java程序让Jvm崩溃

    今天在书上看到一个作者提出一个问题“怎样通过编写Java代码让Jvm崩溃”,我看了之后也不懂.带着问题查了一下,百度知道里面有这样一个答案: package jvm; public class Cra ...

  3. Java 7 jstat – JVM Statistics Monitoring Tool【翻译】

    原文地址:Java 7 jstat 本文内容 语法 参数 描述 虚拟机标识符 选项 一般选项 输出选项 示例 先发出来,然后慢慢翻译~ 语法 jstat [ generalOption | outpu ...

  4. java源代码分析----jvm.dll装载过程

    简述众所周知java.exe是java class文件的执行程序,但实际上java.exe程序只是一个执行的外壳,它会装载jvm.dll(windows下,以下皆以windows平台为例,linux下 ...

  5. Java虚拟机(JVM)中的内存设置详解

    在一些规模稍大的应用中,Java虚拟机(JVM)的内存设置尤为重要,想在项目中取得好的效率,GC(垃圾回收)的设置是第一步. PermGen space:全称是Permanent Generation ...

  6. 写Java程序让Jvm崩溃

    package jvm; public class HeapCrash { public static void main(String[] args) { //Object[] o = {“abc” ...

  7. [Java] Java 技术和 JVM 基础

    Java 由 Sun 公司在 1995 首次发布,既是一门编程语言,也是一个计算平台. Java 运行时版本 Java Runtime Edition 当你下载 Java 完时候,你会得到一个 Jav ...

  8. Java对象在JVM中的生命周期

          当你通过new语句创建一个java对象时,JVM就会为这个对象分配一块内存空间,只要这个对象被引用变量引用了,那么这个对象就会一直驻留在内存中,否则,它就会结束生命周期,JVM会在合适的时 ...

  9. java虚拟机学习-JVM调优总结-分代垃圾回收详述(9)

    为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...

随机推荐

  1. 爬虫-使用BeautifulSoup4(bs4)解析html数据

    Beautiful Soup 是一个HTML/XML的解析器,主要的功能也是如何解析和提取 HTML/XML 数据. 一.安装 sudo pip3 install beautifulsoup4 二.使 ...

  2. java例题_01 不死神兔!

    1 /*1 [程序 1 不死神兔] 2 题目:古典问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少? 3 程 ...

  3. java面试-谈谈你对volatile的理解

    一.volatile特性: volatile是Java虚拟机提供的轻量级的同步机制.主要有三大特性: 保证可见性 不保证原子性 禁止指令重排序 1.保证可见性 1)代码演示 AAA线程修改变量numb ...

  4. UnitThreeSummary

    目录 一.JML的梳理与总结 二.SMT Solver的部署与验证 三.JMLUnitNG的部署与测试 四.作业的设计与总结 第一次作业 第二次作业 第三次作业 五.BUG 六.总结与反思 一.JML ...

  5. Spring Boot 实现配置文件加解密原理

    Spring Boot 配置文件加解密原理就这么简单 背景 接上文<失踪人口回归,mybatis-plus 3.3.2 发布>[1] ,提供了一个非常实用的功能 「数据安全保护」 功能,不 ...

  6. 2-69.x的平方根

    题目描述: 解题思路: 计算平方根可以依次通过自然数递增,来判断两者相乘是否为目标值,是一个有序的序列,因此考虑使用二分查找. 由于x=0和1时,就是其本身,单独拿出来.当x>1时,其平方根一定 ...

  7. 自动化kolla-ansible部署ubuntu20.04+openstack-victoria之镜像制作centos7.8-15

    自动化kolla-ansible部署ubuntu20.04+openstack-victoria之镜像制作centos7.8-15 欢迎加QQ群:1026880196 进行交流学习   制作OpenS ...

  8. mysql架构与存储引擎 (Myisam与Innodb)

    mysql抽象架构:可以分为SQL Layer和Storage Engine Layer mysql的engine层是基于表的,不是基于库的,创建表的语句可以指定engine Mysql的架构 Mys ...

  9. 某大佬的TODOLIST

    回文串 manacher(完成时间:2018.12.10)回文串计数最长双回文串(完成时间:2018.12.10) 扫描线 棋盘制作巨大的牛棚玉蟾宫某个blog 汉诺塔相关 新汉诺塔SHOI 博弈论 ...

  10. SpringCloud(四)GateWay网关

    GateWay网关 概述简介 Gateway是在 Spring生态系统之上构建的AP网关服务,基于 Spring5, Spring Boot2和 Project Reactor等技术. Gateway ...