Skip to content

原文地址:https://www.cnblogs.com/cnki/p/8933996.html

概念

image.png 根据《Java 虚拟机规范》中的说法,Java 虚拟机的内存结构可以分为公有和私有两部分。 公有指的是所有线程都共享的部分,指的是 Java 堆、方法区、常量池。 私有指的是每个线程的私有数据,包括:PC寄存器、Java 虚拟机栈、本地方法栈

内存模型 image.png JAVA的JVM的内存可分为3个区:堆(heap)堆栈(stack)方法区(method)本地方法栈(Native Stack)程序计数器(PC Register)堆区(全局共享): 提供所有类实例和数组对象存储区域 jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 栈区(线程独有): 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 方法区(全局共享): 方法区也是堆的一个组成部分,它主要存储的是运行时常量池、字段信息、方法信息、构造方法与普通函数的字节码内容以及一些特殊方法。它与JAVA堆的区别除了存储的信息与JAVA堆不一样之外,最大的区别就是这一部分JAVA虚拟机规范不强制要求实现自动内存管理系统(GC)。 本地方法栈(线程独有): 与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。(栈的空间大小远远小于堆) 程序计数器(线程独有): 它记载着每一个线程当前运行的JAVA方法的地址,如果是当前执行的是本地方法,则程序计数器会是一个空地址。它的作用就是用来支持多线程,线程的阻塞、恢复、挂起等一系列操作,直观的想象一下,要是没有记住每个线程当前运行的位置,又如何恢复呢。依据这一点,每一个线程都有一个程序计数器,也就是说程序计数器是线程独有的。

堆和栈的区别

image.png JVM 中堆和栈属于不同的内存区域,使用目的也不同。

  • 栈常用于保存方法帧和局部变量,而对象总是在堆上分配。
  • 栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
  • 栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。
  • 堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
  • 栈是运行时的单位,而堆是存储的单位。
  • 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。

在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。

包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。

堆中存的是对象。栈中存的是基本数据类型和堆中对象的引用。一个对象的大小是不可估计的,或者说是可以动态变化的,但是在栈中,一个对象只对应了一个4btye的引用(堆栈分离的好处:))。

为什么不把基本类型放堆中呢?因为其占用的空间一般是1~8个字节——需要空间比较少,而且因为是基本类型,所以不会出现动态增长的情况——长度固定,因此栈中存储就够了,如果把他存在堆中是没有什么意义的(还会浪费空间,后面说明)。可以这么说,基本类型和对象的引用都是存放在栈中,而且都是几个字节的一个数,因此在程序运行时,他们的处理方式是统一的。但是基本类型、对象引用和对象本身就有所区别了,因为一个是栈中的数据一个是堆中的数据。