JVM详细结构

1, JVM整体结构详解

2, Java代码编译和执行过程

编译流程

类加载器

  • Bootstrap ClassLoader: 用来加载jdk中特定的jar包, rt.jar: runtime, 运行时环境jar包
  • Extension ClassLoader: 用来加载扩展类jar包
  • App ClassLoader: 加载应用程序的jar包, 即应用程序真正有用的类加载器
  • Custom ClassLoader: 自定义的加载器, 用于加载指定的java文件

加载流程

  • Loading: 类的信息从文件中获取并载入到JVM的内存中
  • Verifying: 验证, 检测读入的结构是否符合JVM规范的描述, 不符合不加载
  • Preparing: 分配一个结构存储类的所有信息
  • Resolving: 把类的常量池中的所有符号引用改变成直接引用
  • Initializing: 初始化, 执行静态初始化程序, 把静态变量初始化成指定的值

3, 内存管理

Java栈区

作用: 存放Java执行时的所有数据
组成: 由栈帧组成, 一个栈帧代表一个方法的执行

栈帧:

  • 每个方法从调用到执行完成 就 对应一个栈帧在 虚拟机栈中入栈出栈
    – 比如: a方法在运行调用b方法时, java虚拟机就会创建一个保存b方法的栈帧, 然后把这个栈帧压入到java栈区中, 当b方法执行完成返回到a方法时, 这个栈帧则会随之弹出java栈区
  • 栈帧包含: 局部变量表, 栈操作数, 动态链接, 方法出口
    – 应用中常见的栈溢出(stack overflow)异常, 即当栈的深度大于JVM所允许的最大深度时就会抛出该异常. 一个没有退出方法的递归元素即可产生该异常.

本地方法栈

作用: 即专门为Native方法服务
它与栈区一样也是通过栈帧记录方法的调用

方法区

存储被虚拟机加载的类信息, 常量, 静态变量, 即时编译器编译后等数据, 并且永远占据内存

堆区

作用: 所有通过new常见的对象的内存都在堆中分配
特点: 它是虚拟机中最大的一块内存, 是GC要回收的部分

堆区内存图:

  • Young Generation: 表示新生代区
  • Old Generation: 表示老年代区
  • 区别
    – 刚创建的对象全部放入Young Generation内存区, 当Young Generation内存区不足时java虚拟机通过一定的规则将Young区的对应移到Old Generation内存区, 使Young Generation足够空间提供给其他创建的对象, 当YoungGeneration和OldGeneration都没有足够内存时JVM会抛出OutMemary异常, 并且是垃圾回收器重点回收的两个区域
  • 为什么分为新生代区和老生代区两部分而不是一个完整区域:
    – 方便开发人员动态调整两个区域的大小, 比如当开发聊天类应用时新创建的message较多, 可以增大新生代区的内存减小老生代区内存, 便于加快对象的创建; 而开发大型服务类程序并不需要频繁创建对象时可以适当减小新生代区而增加老生代区, 达到对象常驻内存包装服务的稳定性.

4, 垃圾回收

4.1, 垃圾收集算法

引用计数算法:

该算法是java虚拟机最早(1.2版本以前)使用的算法

当在内存中创建一个对象时会对该对象生成一个引用计数器, 同时将引用计数器+1, 每当有新引用引用到该对象时引用器累计+1, 当其中一个引用销毁时引用计数器-1, 当引用器减为0时表示该对象已沦为垃圾对象, 可被回收

引用计数器最大的一个缺陷是当两个对象相互引用并未被其他引用引用时它们的计数器不会为0, 但他们已经是垃圾了, 却不能被回收

可达性算法

从jdk1.2以后都是用的该算法, 也叫根搜索算法, 即把程序中的引用关系看着一张, 从根节点(GC root)开始寻找对应的引用子节点, 再从子节点遍历下去, 当所有引用节点遍历完成后, 剩余未被遍历到的节点被视为不可达的节点, 并被视为垃圾对象

4.2, 对象引用类型

  • 强引用, 软引用, 弱引用, 虚引用
  • 最常使用的是强引用弱引用

弱引用的创建:

Object obj = new Object(); //强引用
WeakReference<Object> wr = new WeakReference<Object>(obj); //弱引用, 相当于创建了obj的弱引用
obj = null;
wr.get(); //可能为null, 因为可能已经被回收

4.3, 垃圾回收算法

标记-清除算法

从上图可看到, 从根集合(根节点)开始遍历引用, 遍历到A再到C, 此时B未被引用, 然后扫描B对象并被标记为可回收对象, 当垃圾回收对象执行时会把B清除掉

优点: 不需要进行对象的移动, 并且仅对不存活的对象进行处理, 在存活对象比较多的情况下极为高效
缺点: 由于标记-清除算法直接回收不存活的对象, 因此会造成内存碎片, 不利于后续对象分配

复制算法

从根集合开始遍历, 当遍历到A引用可达是便把A引用复制到另一块空闲内存中,继续往下遍历发现B不可达便跳过, 然后遍历下一个引用, 只要可达的引用都复制到之前开辟的可达内存区域, 当遍历完整棵树时便把原来的引用内存清空, 保留赋值后的内存空间. 这样达到内存回收

优点: 当存活的对象比较少时极为高效
缺点: 成本大, 需要额为分配一块内存作为交换空间进行对象移动

标记-整理算法

从根集合开始遍历, 通过扫描整个内存区中可回收对象并标记为可回收对象, 最后清除垃圾对象并把后面的引用向前移动.

该算法是在标记-清除算法上的改进, 在回收不存活的的对象占用空间时, 将所有存活对象往左端移动并更新对应指针, 成本更好, 但解决了碎片化问题

以上三种算法各有优缺点, 在虚拟机中会结合不同的场景使用不同的算法

4.4, 垃圾回收触发时间

  • java虚拟机无法再为新的对象分配内存空间了
  • 手动调用System.gc()方法(强烈不推荐)
    –手动调用虚拟机并不会立即去调用垃圾回收
  • 低优先级的GC线程, 被运行时就会执行GC
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM(Java虚拟机)内存结构是Java程序运行的基础,它主要由以下五个部分组成:堆(Heap)、方法区(Method Area)、程序计数器(Program Counter Register)、虚拟机栈(Java Virtual Machine Stacks)和本地方法栈(Native Method Stack)。 1. 堆(Heap) 堆是Java虚拟机中最大的一块内存区域,也是Java程序中最主要的内存区域,用于存放Java程序中的对象实例以及数组。堆内存是所有线程共享的,因此在多线程环境下,需要考虑线程安全问题。 堆内存又分为新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)。其中,新生代用于存放新创建的对象,老年代用于存放长时间存活的对象,而永久代用于存放常量池和类信息等,它的大小是固定的。 2. 方法区(Method Area) 方法区也称为永久代,用于存放Java类和其静态成员变量、常量、方法等信息。方法区同样是所有线程共享的,但是它的大小是固定的,并且不会被自动回收。如果方法区的空间不足,那么就会抛出OutOfMemoryError异常。 3. 程序计数器(Program Counter Register) 程序计数器是一块较小的内存区域,用于存储当前线程执行的字节码指令的地址。每个线程都有自己的程序计数器,它们是独立的,互不干扰。程序计数器的作用是保证线程执行指令的顺序和正确性。 4. 虚拟机栈(Java Virtual Machine Stacks) 虚拟机栈也是线程私有的,用于存放Java方法执行的局部变量、操作数栈、方法出口等信息。每个方法在执行时都会创建一个栈帧,栈帧包含了方法的参数、局部变量以及执行完毕后返回结果的地址等信息。如果虚拟机栈的空间不足,那么就会抛出StackOverflowError异常,如果虚拟机栈的空间已经用尽,那么就会抛出OutOfMemoryError异常。 5. 本地方法栈(Native Method Stack) 本地方法栈与虚拟机栈类似,但是它用于存放Java程序调用本地方法(Native Method)时的参数、局部变量等信息。本地方法栈同样是线程私有的,如果本地方法栈的空间不足,那么就会抛出StackOverflowError异常,如果本地方法栈的空间已经用尽,那么就会抛出OutOfMemoryError异常。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值