JVM之本地方法栈和程序计数器和堆

本文详细介绍了Java虚拟机中的本地方法栈、JNI的作用,以及堆内存的结构、GC机制和内存溢出处理。重点讲解了程序计数器的特点,以及如何通过设置-Xmx调整堆内存大小。
本地方法栈

本地方法栈是为虚拟机执行本地方法时提供服务的

JNI:Java Native Interface,通过使用 Java 本地接口程序,可以确保代码在不同的平台上方便移植

  • 不需要进行 GC,与虚拟机栈类似,也是线程私有的,有 StackOverFlowError 和 OutOfMemoryError 异常

  • 虚拟机栈执行的是 Java 方法,在 HotSpot JVM 中,直接将本地方法栈和虚拟机栈合二为一

  • 本地方法一般是由其他语言编写,并且被编译为基于本机硬件和操作系统的程序

  • 当某个线程调用一个本地方法时,就进入了不再受虚拟机限制的世界,和虚拟机拥有同样的权限

    • 本地方法可以通过本地方法接口来访问虚拟机内部的运行时数据区

    • 直接从本地内存的堆中分配任意数量的内存

    • 可以直接使用本地处理器中的寄存器

原理:将本地的 C 函数(如 foo)编译到一个共享库(foo.so)中,当正在运行的 Java 程序调用 foo 时,Java 解释器利用 dlopen 接口动态链接和加载 foo.so 后再调用该函数

  • dlopen 函数:Linux 系统加载和链接共享库

  • dlclose 函数:卸载共享库


程序计数器

Program Counter Register 程序计数器(物理上由寄存器实现)

作用:内部保存字节码的行号,用于记录正在执行的字节码指令地址(如果正在执行的是本地方法则为空)

原理:

  • JVM 对于多线程是通过线程轮流切换并且分配线程执行时间,一个处理器只会处理执行一个线程

  • 切换线程需要从程序计数器中来回去到当前的线程上一次执行的行号

特点:

  • 是线程私有的

  • 不会存在内存溢出,是 JVM 规范中唯一一个不出现 OOM 的区域,所以这个空间不会进行 GC

Java 反编译指令:javap -v Test.class

#20:代表去 Constant pool 查看该地址的指令

0: getstatic #20        // PrintStream out = System.out;
3: astore_1             // --
4: aload_1              // out.println(1);
5: iconst_1             // --
6: invokevirtual #26    // --
9: aload_1              // out.println(2);
10: iconst_2            // --
11: invokevirtual #26   // --

Heap 堆:是 JVM 内存中最大的一块,由所有线程共享,由垃圾回收器管理的主要区域,堆中对象大部分都需要考虑线程安全的问题

设置堆内存大小:-Xmx

存放哪些资源:

  • 对象实例:类初始化生成的对象,基本数据类型的数组也是对象实例,new 创建对象都使用堆内存

  • 字符串常量池(底层为hashtable结构,不能扩容):

    • 字符串常量池原本存放于方法区,JDK7 开始放置于堆中

    • 字符串常量池存储的是 String 对象的直接引用或者对象,是一张 string table

  • 静态变量:静态变量是有 static 修饰的变量,JDK8 时从方法区迁移至堆中

  • 线程分配缓冲区 Thread Local Allocation Buffer线程私有但不影响堆的共性,可以提升对象分配的效率

设置堆内存指令:-Xmx Size

内存溢出:new 出对象,循环添加字符数据,当堆中没有内存空间可分配给实例,也无法再扩展时,就会抛出 OutOfMemoryError 异常

堆内存诊断工具:(控制台命令)

  1. jps:查看当前系统中有哪些 Java 进程

  2. jmap:查看堆内存占用情况 jhsdb jmap --heap --pid 进程id

  3. jconsole:图形界面的,多功能的监测工具,可以连续监测

在 Java7 中堆内会存在年轻代、老年代和方法区(永久代)

  • Young 区被划分为三部分,Eden 区和两个大小严格相同的 Survivor 区。Survivor 区某一时刻只有其中一个是被使用的,另外一个留做垃圾回收时复制对象。在 Eden 区变满的时候,GC 就会将存活的对象移到空闲的 Survivor 区间中,根据 JVM 的策略,在经过几次垃圾回收后,仍然存活于 Survivor 的对象将被移动到 Tenured 区间

  • Tenured 区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在 Young 复制转移一定的次数以后,对象就会被转移到 Tenured 区

  • Perm 代主要保存 Class、ClassLoader、静态变量、常量、编译后的代码,在 Java7 中堆内方法区会受到 GC 的管理

分代原因:不同对象的生命周期不同,70%-99% 的对象都是临时对象,优化 GC 性能

public static void main(String[] args) {
    // 返回Java虚拟机中的堆内存总量
    long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
    // 返回Java虚拟机使用的最大堆内存量
    long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
    
    System.out.println("-Xms : " + initialMemory + "M");//-Xms : 245M
    System.out.println("-Xmx : " + maxMemory + "M");//-Xmx : 3641M
}

### JVM内存模型详解 #### (Heap) JVM中最大的一块内存区域,也是所有线程共享的内存部分。几乎所有对象实例数组都在这里分配内存[^4]。从垃圾回收的角度看,通常被划分为新生代老年代两个主要区域: - **新生代**: 主要用于存放新创建的对象。进一步细分为 Eden 空间、From Survivor To Survivor 空间。 - 新生代中的大部分对象会在第一次 GC 后消失,只有少数存活下来的对象会被移动到幸存者空间。 - **老年代**: 存放生命周期较长的对象。经过多次 GC 后仍然存活的对象会被晋升到这里。 还是垃圾收集器管理的核心区域,不同的垃圾回收算法(如标记清除法、复制法、标记整理法)会对这些区域进行清理以释放无用对象占用的空间。 --- #### (Stack) 每个线程启动时都会为其创建一个独立的,称为 Java 虚拟机是由一系列帧组成的,每执行一个方法就会压入一个新的帧,退出方法则弹出对应的帧[^3]。内存主要用于存储局部变量表、操作数、动态链接其他信息。 如果线程请求的深度大于允许的最大深度,则会抛出 `StackOverflowError` 错误;而如果虚拟机无法扩展所需的内存大小,则会抛出 `OutOfMemoryError` 错误。 --- #### 方法区 (Method Area) 方法区是一个逻辑上的概念,在 JDK 8 之前被称为永久代 (Permanent Generation),位于的一部分,用来存储已被加载的类的信息、常量池以及其他静态数据[^5]。然而,自 JDK 8 开始,永久代已经被元空间 (Metaspace) 替代,后者使用的是本地内存而非内存来存储类的相关元数据。 这种方法的变化有效缓解了因永久代容量有限而导致的各种问题,并使得开发者可以通过调整参数更灵活地控制元空间的增长行为。 --- #### 本地方法 (Native Method Stack) 类似于 Java 虚拟机,但它服务于原生方法调用而不是 Java 方法。每当一个线程被执行时,也会相应地建立其专属的本地方法[^1]。对于大多数实现而言,该的行为与普通 Java 非常相似,只是它处理的内容涉及 C/C++ 编写的函数或其他非 Java 的代码片段。 --- #### 程序计数器 (Program Counter Register) 这是一个较小的内存单元,记录当前线程所执行字节码指令的位置。由于 Java 支持多线程并发运行,因此每个线程都有自己单独的程序计数器,互不干扰。值得注意的是,如果是 Native 方法调用的话,那么此时程序计数器值为空 (`undefined`)。 --- ### 垃圾回收机制 (Garbage Collection) 垃圾回收的目标是从不再使用的对象中自动回收它们占据的内存资源,从而减少手动管理内存带来的复杂性潜在风险。常见的几种垃圾回收策略如下: - **串行收集器(Serial Collector)**: 单线程工作模式下完成整个过程,适合单处理器客户端应用环境。 - **并行收集器(Parallel Collector)**: 利用了多核 CPU 提高性能,适用于吞吐量优先场景下的服务器端应用程序。 - **CMS(Copy Mark Sweep) 收集器**: 致力于降低暂停时间,通过并发阶段尽可能让应用程序继续运作的同时进行垃圾回收活动。 - **G1(Garbage First) 收集器**: 将分成若干个大小相等的小块,按照预测收益最高的原则决定先清扫哪些区块,兼顾延迟与效率平衡。 不同类型的垃圾回收器针对特定需求进行了优化设计,选择合适的方案取决于具体业务特性及其对响应时间吞吐率的要求。 ```java // 示例:启用 G1 垃圾回收器 public class Main { public static void main(String[] args) { System.setProperty("java.vm.options", "-XX:+UseG1GC"); // 应用逻辑... } } ``` 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

向着五星的方向

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值