美文网首页
JVM学习记录

JVM学习记录

作者: Kraos | 来源:发表于2019-07-31 15:27 被阅读0次
    • 数据是商品,硬盘是仓库,内存是货架,买东西是只能在货架上买的

    • 货架容纳不下当前需要摆放的商品,即内存溢出

    • JVM 虚拟出一个计算机来仿真模仿各种计算机功能

    • JVM 有自己的硬件架构,如处理器、堆栈、寄存器等,还有对应分指令系统

    • JVM是淘宝店铺,不是真是存在的货架但能上架下架商品,数量也是有限

    • 平台无关性可以理解成异地购物

    • 内存模型就是具体的各变量访问规则

    • 所有变量保存主内存,每条线程有自己的工作内存保存用到变量的内存副本

    • 不能直接读写内存的变量

    • 不同线程无法访问对方的工作内存变量,值传递要通过主内存进行

    • 执行引擎相当于CPU

    • 8种操作完成主内存与工作内存的交互协议

    • lock锁定变量,将其变成一条线程独占

    • unlock 解锁

    • read 将主内存变量传输到线程的工作内存 -> load 将取得变量放入变量副本

      主内存->read->load->工作内存

    • write 将store得到的变量副本写入到主内存

      工作内存->store->write->主内存

    • use 将工作内存的变量传递给执行引擎

      工作内存->use->执行引擎

    • assign 将执行引擎收到的值赋给工作内存

      执行引擎->assign->工作内存

    • store 将工作内存的变量传送到主内存供write使用

    • JVM内存划分:线程私有,线程共享

    • 共享:方法区,堆

    • 私有:虚拟机栈,程序计数器,本地方法栈

    • 局部变量表会随着函数栈帧的销毁而销毁,包括基本数据类型、对象引用、字节码指令地址

    • 操作栈保存计算过程的中间结果

    • 每个栈帧包含一个指向运行时常量池中该帧所属方法引用,持有该引用是为了动态连接

    • 正常完成出口,异常完成出口,无论哪种形式推出,都需要返回到方法被调用位置

    程序计数器

    • 程序计数器是一块较小的内存空间,可以看作是当前线程执行的字节码的行号指示器。

    • 为了线程切换后能恢复到正确的执行位置,每条线程都有一个私有的计数器。

    • 程序计数器是唯一一个在 Java 虚拟机规范中没有规定任何 OOM 情况的区域。

      虚拟机栈

    • 虚拟机栈就是Java方法栈,每个方法执行时创建一个栈帧(Stack Frame),栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,生命周期与线程相同。

    • 虚拟机栈的两种异常:StackOverflowError(栈请求深度大于规定的最大深度),OutOfMemoryError(扩展申请不到足够的内存)

    本地方法栈(Nativie Method Stack)

    • Native方法服务
    • 虚拟机栈一样的异常

    Java堆

    • 存放创建的对象实例 即是所有的变量
    • 最大的内存区域
    • 虚拟机创建时创建
    • GC管理的主要区域

    方法区

    • 线程共享
    • 已被虚拟机加载的数据
    • 存储类型:类信息 常量 静态变量 即时编译器编译后的代码
    • 此区大小决定可以保存多少个类,定义过多溢出,会抛出OutOfMemoryError
    • 分运行时常量池和直接内存
    • Class 文件中除了有类的版本、字段、方法和接口等描述信息,还有一项信息就是常量池(Constant Pool Table)。 常量池->运行时常量池
    • 直接内存
    • 直接内存的分配不会受到 Java 堆大小的限制,但是会受到设备总内存(RAM 以及 SWAP 区)大小以及处理器寻址空间的限制。
    • 通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作,这样能避免在 Java 堆和 Native 堆中来回复制数据。
    • 默认容量与Java堆最大值一样

    栈帧

    • 方法的调用链路由Java一个个方法栈帧组成
    • 一个栈帧包括了局部变量表、操作栈、动态连接、返回地址
    • 局部变量表中的数据在函数调用结束后随着栈帧的销毁而销毁
    • 操作数栈保存计算过程的中间结果,临时变量存储的地方
    • 动态连接指执行运行时常量池中该栈帧所属方法引用
    • 返回地址:正常完成出口,异常完成出口,无论是哪种推出方式,都要返回到方法被调用位置,才能继续执行

    可达性算法

    • 在主流的商用程序语言(Java、C# 和 Lisp 等)的主流实现中,都是通过可达性分析(Reachability Analysis)判定对象是否存活的。
    • 当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。

    引用相关

    • 在 JDK 1.2 之后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用和虚引用四种,这四种引用强度按顺序依次减弱。

    • 强引用是指代码中普遍存在的,比如 "Object obj = new Object()" 这类引用。直接访问,不会回收(即使OOM),可能内存泄漏

    • 对于软引用关联的对象,在系统即将发生内存溢出前,会把这些对象列入回收范围中进行二次回收。如果二次回收后还没有足够的内存,就会抛出内存溢出异常。在 JDK 1.2 后,Java 提供了 SoftReference 类来实现软引用。(这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。)

      SoftReference<String> sr = new SoftReference<String>(new String("hello"));
      
    • 弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。(biss)如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象

      WeakReference<String> sr = new WeakReference<String>(new String("hello"));
      
    • 虚引用在java中用java.lang.ref.PhantomReference类表示。

    • 虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。

    • 当我们操作不当导致某块内存泄漏时,GC 就不能对这块内存进行回收。

    • 拿 Android 来说,进行 GC 时,所有线程都要暂停,包括主线程,16ms 是 Android 要求的每帧绘制时间,而当 GC 的时间超过 16ms,就会造成丢帧的情况,也就是界面卡顿。

    垃圾回收算法

    • 标记-清除算法(Mark-Sweep) 标记所有商品的状态没人买的就下架 :效率低,标记和清除的效率都不高;内存碎片太多会导致当程序需要分配较大的对象时,无法找到足够的连续内存而不得不提前触发 GC

    • 复制(Copying)收集算法 可用内存按容量划分为大小相等的两块,这块内存用完了,就把存活的对象复制到另一块内存上,然后把已使用的空间一次清理。:每次都是对半个内存区域进行回收,内存分配时也不用考虑内存碎片等复杂问题。

    • 浪费空间

      把内存缩小一半来使用太浪费空间。

    • 有时效率较低

      在对象存活率高时,要进行较多的复制操作,这时效率就变低了

    相关文章

      网友评论

          本文标题:JVM学习记录

          本文链接:https://www.haomeiwen.com/subject/bfltdctx.html