JVM内存结构与Java NIO优化实践

JVM内存区域

首先,我们先回忆下, JVM结构中内存区域可以包括哪几个部分?

JVM(Java虚拟机)的内存结构主要分为以下几个部分,每个部分负责不同的功能,共同支撑Java程序的运行:


1. 程序计数器(Program Counter Register)

  • 作用
    • 记录当前线程执行的字节码指令地址(行号)。
    • 线程私有,确保多线程切换后能恢复到正确执行位置。
  • 特点
    • 唯一不会抛出 OutOfMemoryError 的内存区域。

2. Java虚拟机栈(Java Virtual Machine Stacks)

  • 作用
    • 存储方法的栈帧(Frame),包括局部变量表、操作数栈、动态链接、方法出口等。
    • 每个方法调用对应一个栈帧,方法结束时栈帧弹出。
  • 线程私有:每个线程有独立的虚拟机栈。
  • 异常
    • StackOverflowError:栈深度超过限制(如无限递归)。
    • OutOfMemoryError:栈扩展失败(如线程过多)。

3. 本地方法栈(Native Method Stack)

  • 作用
    • 为JVM调用本地(Native)方法(如C/C++代码)提供服务。
  • 与虚拟机栈的区别
    • 虚拟机栈执行Java方法,本地方法栈执行Native方法。
  • 异常:同虚拟机栈(StackOverflowError/OutOfMemoryError)。

4. Java堆(Java Heap)

  • 作用
    • 存储所有对象实例和数组(通过 new 创建的对象)。
    • 垃圾回收(GC)的主要区域。
  • 线程共享:所有线程共享堆内存。
  • 分代设计(JDK8及之前):
    • 新生代(Young Generation):
      • Eden区、Survivor区(From/To)。
      • 存放新创建的对象,Minor GC回收。
    • 老年代(Old Generation):
      • 存放长期存活的对象,Major GC/Full GC回收。
    • 元空间(Metaspace,JDK8+):
      • 替代永久代(PermGen),存储类元数据、方法区信息。
  • 异常OutOfMemoryError(堆空间不足)。

5. 方法区(Method Area)

  • 作用
    • 存储类信息(类名、字段、方法、常量池等)、静态变量(static)、即时编译器编译后的代码。
  • 实现变化
    • JDK7及之前:永久代(PermGen)实现。
    • JDK8+:元空间(Metaspace)替代,使用本地内存(Native Memory)。
  • 异常OutOfMemoryError(如加载过多类)。

理清JVM和Native的栈和堆

有没有注意到,上面的栈包括2种:一种是JVM栈,一种是本地(Native)方法栈。
本地方法为什么需要独立的栈?

  • 语言差异:Native方法通常由C/C++实现,其调用约定(Calling Convention)、栈帧结构与Java方法不同。
  • 线程安全:每个线程需要独立的Native栈,避免多线程调用Native方法时上下文混乱。

因此,本地方法栈虽然使用操作系统的Native内存,但由JVM统一规划和管理(如线程创建时分配栈大小,线程结束时释放)。

也就是说程序运行过程中,调用JNI方法会切换到本地方法栈,从native调用java方法又会切换到Jvm栈。

本地方法栈可以认为是JVM管理的Native内存,调用JNI方法切换到本地方法内分配的堆这部分内存又是谁管理的?
直接分配的Native堆(如malloc/new分配的内存),开发者手动管理(需显式free/delete),是开发者或第三方库通过系统调用分配的完全独立的Native内存。因此JVM不参与管理,需手动防止泄漏。

直接内存(堆外内存)

对于java而言,除了上面5个内存区域,其实还有一个概念,那就是直接内存。什么是直接内存?简单说就是通过java的方法分配的Native的堆内存(Java堆外直接内存)。由操作系统的本地内存(Native Memory)管理的。它受 -XX:MaxDirectMemorySize 参数限制,可以看作 JVM 允许 Java 代码使用的受控本地内存.

直接内存的优缺点

优点

  1. 减少拷贝:避免JVM堆与Native堆之间的数据复制(如SocketChannel发送数据时无需复制到临时缓冲区)。
  2. 提升吞吐量:适合大规模、高频I/O操作(如Netty等网络框架默认使用Direct Buffer)。
  3. 绕过GC压力:大块内存分配在堆外,减少Full GC触发频率。

缺点

  1. 分配成本高:Direct Buffer的创建和销毁比Heap Buffer慢(涉及系统调用)。
  2. 内存泄漏风险:若被引用导致无法触发gc,或忘记释放(或Cleaner未触发),会导致Native堆内存泄漏。所以建议手动调用clean方法来释放。
  3. 调试困难:堆外内存问题通过常规比较难以直接观测。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值