Java 虚拟机笔记

JVM 运行时数据分区

根据 Java 虚拟机规范 Run-Time Data Areas 章节的描述,Java 虚拟机的运行时数据划分如下:
在这里插入图片描述

  • 程序计数器/pc寄存器-PC(program counter) Register
    线程私有。 程序计数器包含线程当前正在执行的Java虚拟机指令的地址。 如果线程当前正在执行的方法是本地方法,则Java虚拟机的pc寄存器的值未定义。

  • Java 虚拟机栈
    每个Java虚拟机线程都有一个私有Java虚拟机栈,与该线程同时创建。Java 虚拟机栈中保存的数据结构是栈帧——每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。与 Java 虚拟堆栈相关的异常有两个:

    • StackOverflowError
      如果线程运行过程中压栈超过了Java虚拟机栈允许的最大深度(如递归条件不当,导致无限递归调用),则Java虚拟机将引发StackOverflowError。
    • OutOfMemoryError
      如果没有足够的内存来扩展Java虚拟机堆栈;或者没有足够的内存来为新线程创建初始Java虚拟机堆栈,则Java虚拟机机器抛出OutOfMemoryError。
  • 本地方法栈-Native Method Stacks
    线程私有。和 Java 虚拟机栈类似,Java虚拟机的一种实现,用传统的堆栈实现方式来支持本地方法(非 Java 语言编写的方法)调用。通常在创建每个线程时为每个线程分配本地方法栈。

  • 方法区
    方法区是在虚拟机启动时创建的,所有虚拟机线程共享。它存储每个类的结构,如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化以及接口初始化的特殊方法。与方法区相关的异常:

    • OutOfMemoryError
      如果方法区内存无法满足分配请求,则Java虚拟机将引发OutOfMemoryError。
    • 运行时常量池
      方法区的一部分。用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后,进入方法区的运行时常量池中存放。运行时常量池还具有动态性的特征——常量不一定只有编译期才能产生,运行期间也可能将新的常量放入池中,这种特性用得比较多的便是String类的intern()方法。与运行时常量池有关的异常:
      • OutOfMemoryError
        创建类或接口时,如果构造运行时常量池所需的内存超过Java虚拟机的方法区中可用的内存,则Java虚拟机将引发OutOfMemoryError。

  • 堆在虚拟机启动时创建,所有虚拟机线程共享。堆是运行时数据区,分配所有类实例和数组的内存,也是 GC 进行垃圾回收的主要区域。与堆有关的异常:

    • OutOfMemoryError
      如果申请的堆内存大于系统可以提供的堆,则Java虚拟机将引发OutOfMemoryError。

    为了更好地回收内存,堆内存还有更细致的划分:

    • 年轻代
      • Eden
      • From Survivor
      • To Survivor
    • 老年代
      在这里插入图片描述

垃圾回收算法

标记-清除算法

分为两个阶段:

  1. 从 GC Root 节点出发,根据可达性分析,将内存对象标记为存活对象或垃圾对象;
  2. 将所有垃圾对象清除
    优点:简单,不需要移动对象。
    缺点:容易产生内存碎片,提高了内存回收频率

复制算法

将内存一分为二,每次只使用其中一半,内存回收时,将存活内存复制到另一半中,然后清除当前这一半的所有内存。
优点:不会产生垃圾碎片。
缺点:可用内存减少为一半,对象存活率高时,会频繁复制。

标记-压缩算法

分为两个阶段:

  1. 从 GC Root 节点出发,根据可达性分析,将内存对象标记为存活对象或垃圾对象;
  2. 将存活对象复制到内存的一端,然后将端边界以外的内存全部清除。
    优点:不会产生内存碎片,也不用讲内存分块。
    缺点:所谓压缩操作,还是会复制对象。

常见 JVM 参数选项

详细参数描述请参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#CBBIJCHG
JVM 支持分为好几个类别的许多参数选项,详情参考上面的文档。常用的参数类型有:

  • -X 开头的:非标准选项。是特定于Java HotSpot虚拟机的通用选项,因此不能保证所有JVM实现都支持它们,并且它们可能会发生变化。
  • -XX 开头的:高级运行时选项。开发人员选项,用于调整Java HotSpot虚拟机操作的特定区域。使用的方式有如下3种:
    • -XX:+<option> 启用option参数;
    • -XX:-<option> 禁用option参数;
    • -XX:<option>=<value> 将option参数的值设置为value。

堆内存相关参数

参数含义示例
-Xms堆的初始内存大小-Xms10m 初始化堆大小为10M
-Xmx堆的最大内存-Xmx20m 堆占用的最大内存为20M
-Xmn设置新生代内存的大小-Xmn10m 设置新生代内存的大小为10M
-XX:SurvivorRatio设置年轻代中 Eden 区域和 Survivor 区域的比例
-XX:+PrintGCDetails打印 GC 的详细 log 信息
-XX:HeapGrowthLimit
-XX:HeapMaxFree单次 Heap 内存调整的最大值
-XX:HeapMinFree单次 Heap 内存调整的最小值
-XX:HeapTargetUtilization设置理想的堆内存利用率,

关于 Android 中的 largeHeap 选项

在 Android 中,如果我们希望应用运行时能使用更多的内存,可以在 Manifest 文件的 application 节点中,添加一个 android:largeHeap="true" 的配置。那添加了这个配置后,能比普通应用多用多少内存呢?我们可以通过以下两种方式得到答案:

  1. 运行时代码获取(Kotlin)
val am = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
// 普通应用可以使用的最大内存,单位:MB
val memory = am.memoryClass
// largeHeap 应用可以使用的最大内存,单位:MB
val largeMemory = am.largeMemoryClass
  1. 通过 adb 命令查看 DVM/ART 虚拟机 heap 相关的系统属性
$ adb shell getprop | grep vm.heap
[dalvik.vm.heapgrowthlimit]: [384m]        # 相当于 JVM -XX:HeapGrowthLimit。=am.memoryClass
[dalvik.vm.heapmaxfree]: [8m]              # 相当于 JVM -XX:HeapMaxFree 选项
[dalvik.vm.heapminfree]: [512k]            # 相当于 JVM -XX:HeapMinFree 选项
[dalvik.vm.heapsize]: [512m]               # 相当于 JVM -Xmx 选项。=am.largeMemoryClass
[dalvik.vm.heapstartsize]: [8m]            # 相当于 JVM -Xms 选项
[dalvik.vm.heaptargetutilization]: [0.75]  # 相当于 JVM -XX:HeapTargetUtilization 选项,用于设置理想的堆内存利用率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值