综述

Java虚拟机的内存模型主要分为以下七个部分:

整体结构如图

图片说明

线程私有内存

每个线程内部的空间,不同线程之间不能相互访问。

程序计数器

  • 指令读取顺序
  • 线程执行位置

Java虚拟机栈

java内存模型的部分(局部变量表),为执行Java字节码服务。

  • 栈帧:
    • 局部变量表:各种数据类型、对象的引用
    • 操作数栈
    • 动态链接
    • 方法出口信息
  • 相关异常错误:
    • StackOverflowError:不允许动态扩展内存时,线程请求的栈深度超过JVM最大深度
    • OutOfMemoryError:在允许动态扩展内存时,线程请求栈用完且无法再扩展

本地方法栈

为JVM使用到的Native方法服务。

在编程领域,JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。 --- 维基百科

栈帧和异常同Java虚拟机栈。

线程共有内存区

包含常说的“堆”内存区,为线程共享,不同线程都可以访问。常用来保存静态信息、对象等数据。

所有线程共享,且在JVM启动时创建,用于存放对象实例
是垃圾收集器管理(GC)的主要区域。

方法区

在JVM的规范中,方法区是堆的一个逻辑部分,垃圾收集行为较少出现。

数据:

  • 已被JVM加载的类的信息
  • 常量(常量池)
  • 静态变量
  • 编译后代码

常量池

  • 是方法区的一部分
  • 可以避免频繁的创建和销毁对象影响系统的性能
  • 所有的基本数据类型都有实现常量池
  • 基本类型的包装类中:ByteShortIntegerLongCharacterBoolean在常量池中有缓存,FloatDouble无缓存
  • String对象也会使用常量池优化(使用intern方法可以将堆中的String对象设置到常量池)
          String s = new String("str").intern();
          System.out.println(s == "str");    // true

直接内存

又叫“堆外内存”,是把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。

  • 使用java.nio.DirectByteBuffer进行堆外内存的管理和操作
  • 不受 JVM 堆内存大小限制。
  • 可能出现OutOfMemoryError异常。

优点

  • 减少了垃圾回收
    因为垃圾回收会暂停其他的工作。
  • 加快了复制的速度
    堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。