zoukankan      html  css  js  c++  java
  • java虚拟机

    一、开始

     打算从静态代码开始说起。重点还是后面动态的过程

    从问题出发,为什么可以实现“一次编写,到处运行”?

    答案:平台/语言无关的字节码编译结果(.class文件)+虚拟机

    二、Java对象的一生——出生

    首先,西红柿炒鸡蛋的一生?

    1. 看菜谱,把需要的食材放到一起(加载)

    2. 看下食材有没有坏掉的?锅洗了没?盐、酱油是否有?(验证)

    3. 把鸡蛋洗一下,西红柿也要洗一下切块,鸡蛋则可以打一下,完成最基本的备菜(准备)

    4. 开始打蛋调味,西红柿呢,就进行切块(解析)

    5. 先炒鸡蛋,再炒西红柿(初始化)

    6. 吃饭

    7. 光盘行动~~

    1. 加载

    获取一个类的二进制字节流,最终生成一个Class对象

    • 什么时候需要加载
      • new关键字、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候
      • 使用java.lang.reflect包的方法对类进行反射调用
      • 当初始化一个类的时候,如果发现其父类未初始化
      • 当虚拟机启动时,用户需要指定一个要执行的主类
      • 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化
    • 从哪获取二进制字节流
      • zip、jar、war
      • 网络,如Applet
      • 运行时计算生成,如动态代理技术,在java.lang.reflect.
      • Proxy中,用ProxyGenerator.generateProxyClass来为特定接口生成形式为“*$Proxy”的代理类的二进制字节流
    • 获取到的二进制流如何存储

    方法区生成一个java.lang.Class对象,作为程序访问方法区中的这些类型数据的外部接口

    • 谁来加载——类加载器

    虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器

      • 为什么要放到外面实现:个人觉得主要是出于灵活性的考虑,减少对语言、平台等的限制。虚拟机只要定义规则就好了
      • 那么这么多加载器如何管理——双亲委派模型

      •  规则有问题吗?——破坏双亲委派模型

     JNDI:本身由启动类加载器加载,但是它需要去加载其他的资源,这些资源是用户定义的,无法被启动类加载器认识

    程序动态性:代码热替换(HotSwap)、模块热部署(HotDeployment)

    (OSGi)....这个还要再补充

    2. 连接——验证

    目的:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

    从外往里看,可以看到有以下几个部分

    • 字节流

    是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理

    只有通过这个验证,才能进入方法区存储

    • 类的定义

    如这个类是否有父类、字段是否与父类冲突、是否实现所有抽象方法等

    • 类的方法体
    • 符号引用

    发生在虚拟机将符号引用转化为直接引用的过程

    验证是否能找到引用对象,字段等是否可访问

    3. 连接——准备

    目的:为类(static)变量分配内存并设置类初始值

    非final变量:0、false、null

    final变量:程序中设置的值

    4. 连接——解析

    目的:将常量池内的符号引用替换为直接引用

    符号引用:个人觉得可以看成是占位符

    直接引用:通过直接引用可以找到引用对象的内存位置。可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄

    5. 初始化

    初始化阶段是执行类构造器<clinit>()方法的过程

    <clinit>()方法是由编译器自动收集类中的所有类变量赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问

    三、Java对象的一生——离开

    • 什么时候可以离开——不再“”被使用“”时离开——怎么判断不再被使用?
      • 引用计数算法:增加一个引用则计数加1,引用减少时则减1
      • 可达性分析算法:从root结点(虚拟机栈(栈帧中的本地变量表);方法区中类静态属性;方法区中常量;本地方法栈中JNI(即一般说的Native方法))开始分析是否与某一对象可达
    • 什么时候让无用的对象离开
    • 如何离开

      举个例子:有一堆黄豆,要去除其中坏的去除。基于坏和好数量的不同,应该按照哪个少,把哪个捡出来

      • 标记-清除算法(坏的少):最简单的,有可以离开的对象就让它离开好了
        • 太粗暴,会留下一些空洞(碎片),这些碎片太小无法被再利用,太浪费了(空间利用角度的考虑)
      • 复制算法(坏的多):将一个空间分成两个部分AB,一开始只在A进行分配,清理时把要保留的对象复制到B
        • 把空间一分为二,如果资源并不富裕,这个方案可就不行了
      • 标记-整理算法(空间有限):直接移动要保留的对象(覆盖将移除的内存)到某一端,然后清理掉端边界外的内存
      • 分代收集算法:上面都挺好的,那么就做个结合,各取所需。
        • 新生代——复制,每次垃圾收集时都发现有大批对象死去,只有少量存活
        • 老年代——“标记—清理”或“标记—整理”,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用来进行回收

    参考资料

    1. 《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》

  • 相关阅读:
    623. Add One Row to Tree 将一行添加到树中
    771. Jewels and Stones 珠宝和石头
    216. Combination Sum III 组合总数三
    384. Shuffle an Array 随机播放一个数组
    382. Linked List Random Node 链接列表随机节点
    向github项目push代码后,Jenkins实现其自动构建
    centos下安装Jenkins
    python提取批量文件内的指定内容
    批处理实现:批量为文件添加注释
    python抓取每期双色球中奖号码,用于分析
  • 原文地址:https://www.cnblogs.com/coolqiyu/p/13340802.html
Copyright © 2011-2022 走看看