大家都知道Java能跨平台,是因为JVM,它屏蔽了底层操作系统和物理计算机的差异。Java中还有一个非常重要的概念,JMM(Java Memory Model),Java内存模型,JMM 也是 JVM 规范里定义的一部分,JMM 定义了 JVM 在计算机内存中的工作方式。

Java 内存模型

  • 方法区:线程共享的一个区域,用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在JVM规范里,方法区是堆的一个逻辑部分,不过它还有另外一个名字 Non-Heap(非堆),目的是与Java堆区区分开来。
  • 堆内存:线程共享的一个区域,垃圾收集器管理的主要区域。
  • 虚拟机栈(线程栈内存):Java线程私有。每个方法在执行的时候,都会创建一个栈帧用于存储局部变量、操作数、动态链接、方法出口等信息。每个方法的调用都意味着一个栈帧在虚拟机中入栈到出栈的过程
  • 本地方法栈:跟线程栈区类似,该区域为JVM提供使用的 native 方法的服务
  • 程序计数器:Java线程私有,可以看做当前线程所执行的字节码的行号指示器。(执行Java方法,记录字节码指令的地址;执行Native方法,计数器值为空)。此内存区域是唯一一个在Java虚拟机里没有规定任何 OutOfMemoryError的区域。

JMM主内存&工作内存与JVM的堆栈内存的对应关系

JMM 规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory)。

JMM的主内存和工作内存都是抽象概念,与JVM的内存的堆栈没有直接的关系;不过从定义来看,还是能对应上的,主内存对应就是堆中的对象实例(上图蓝色的部分),工作内存对应是线程栈中的变量(上图紫色的部分)

JMM主内存&工作内存与硬件内存的对应关系

从更底层的硬件内存架构来看,主内存对应的基本是物理内存(RAM),线程中的工作内存对应的是CPU寄存器和高速缓存,因为线程在这里进行变量IO操作性能更好

Java内存的抽象模型

线程中的工作内存都保存了改线程使用的主内存的变量的副本拷贝,线程对于变量的操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量

不同的线程之间无法直接访问对方工作内存中的变量,线程之间值的传递都要通过主内存来完成。

内存泄漏、内存溢出

简单总结

术语理解
内存溢出内存不够用了
内存泄漏指对象可达,但是没用了。就是说本该被GC回收的对象没有被回收,导致既不能用又占用内存
联系内存泄漏是内存溢出的原因之一,内存泄漏堆积起来就会导致内存溢出

内存泄漏原因分析

  1. 长生命周期的对象引用短生命周期的对象。(如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。)
  2. 没有将无用的对象置为null