目录
JVM内存结构
JVM内存结构中哪些是线程私有的? 哪些是内存共享的
JVM内存模型里的堆和栈有什么区别
内存泄漏和内存溢出有什么不同
JVM内存结构
jdk1.6中,方法区的实现是永久代, 而永久代是在堆中的. 方法区存储了类信息Class, 类加载器ClassLoader, 运行时常量池.运行时常量池中存储了字符串常量池String Table
java8中,方法区的实现是元空间, 元空间在本地内存(操作系统内存)中.方法区同样存储了类信息Class, 类加载器ClassLoader, 运行时常量池.而字符串常量池String Table 被放在了堆区
JVM内存结构中哪些是线程私有的? 哪些是内存共享的
线程私有的
程序计数器
程序计数器保存了下一条指令的执行地址, 所以解释器才能读取下一条指令然后执行.
为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器, 所以程序计数器是线程私有的
因为只保存下一条指令的地址, 程序计数器是唯一一个不会出现OutOfMemoryError (内存溢出)的内存区域
虚拟机栈
虚拟机栈的生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡。
每个方法执行时都会创建一个桢栈来存储方法的局部变量表、操作数栈、方法返回地址等信息。栈的大小决定了方法调用的可达深度(如递归多少层次)
虚拟机栈抛出的异常一般是 StackOverflow .
本地方法栈
与虚拟机栈作用相似。虚拟机需要用到c或者c++写的一些本地方法(native修饰的方法), 这些本地方法运行时使用的内存就是本地方法栈.
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、方法返回地址信息。
线程共享的
堆
堆是线程共享的, 堆中的对象需要考虑线程安全问题.
java中几乎所有的对象实例以及数组存储到堆中. 但是随着逃逸分析技术不断成熟, java7已经默认开启了逃逸分析, 如果方法中某些对象没有被返回, 也没有被外界使用(就是没有逃逸出去), 那么对象可以直接在栈(具体是指“虚拟机栈”中的“栈帧”)上分配内存.
堆是垃圾收集器管理的主要区域, 由于现在收集器基本都采用分代垃圾收集算法,从垃圾回收的角度来看, Java 堆还可以细分为:新生代和老年代. 在java8以前, 还有堆中还有永久代,java8之后就永久代被元空间取代, 位置也放到了本地内存中.
方法区
jdk1.6中, 方法区的实现是永久代, 而永久代是在堆中的. 方法区存储了类信息Class, 类加载器ClassLoader, 运行时常量池. 运行时常量池中存储了字符串常量池String Table
java8中, 方法区的实现是元空间, 元空间在本地内存(操作系统内存)中. 方法区同样存储了类信息Class, 类加载器ClassLoader, 运行时常量池. 而字符串常量池String Table被放在了堆区
直接内存(非运行时数据区的一部分)
JVM内存模型里的堆和栈有什么区别
用途
栈主要用于存储局部变量、方法调用的参数、方法返回地址以及一些临时数据。每当一个方法被调用,一个栈帧(stackframe)就会在栈中创建,用于存储该方法的信息,当方法执行完毕,栈帧也会被移除。
堆用于存储对象的实例。当你使用new关键字创建一个对象时,对象的实例就会在堆上分配空间。
生命周期
栈中的数据具有确定的生命周期,当一个方法调用结束时,其对应的栈帧就会被销毁,栈中存储的局部变量也会随之消失。
堆中的对象生命周期不确定,对象会在垃圾回收机制检测到对象不再被引用时才被回收。
存储
栈的空间相小. 当栈溢出时,通常是因为递归过深
堆往往比较大. 堆溢出通常是由于创建了太多的大对象或未能及时收不再使用的对象
可见性
栈中的数据对线程是私有的,每个线程有自己的栈空间。
堆中的数据对线程是共享的,所有线程都可以访问堆上的对象。
内存泄漏和内存溢出有什么不同
内存泄漏是指程序在运行过程中不再使用的对象仍然被引用,从而无法被垃圾收集器回收,导致可用内存逐渐减少
泄漏的本质是:对象应该被回收,但仍有引用指向它,导致GC无法回收
内存泄漏常见原因:
静态集合:使用静态数据结构(如HashMap或ArrayList)存储对象,且未清理
线程:未停止的线程可能持有对象引用,无法被回收
ThreadLocal: ThreadLocal使用后不调用remove, 就可能发生内存泄漏
内存溢出是指JVM在申请内存时,没有足够的内存,最终引发OutofMemoryError。往往发生在堆内存不足以存放新创建的对象时.
内存溢出常见原因:
大量对象创建:短时间程序中不断创建大量对象, 且无法回收, 超出JVM堆的限制就会OOM.
内存泄漏: 内存泄漏之后, 可用内存减少, 一直泄漏下去, 就很容易出现OOM.
持久引用:大型数据结构(如缓存、集合等)长时间持有对象引用,导致内存累积.
递归调用:深度递归导致栈溢出, 栈溢出也是内存溢出的一种.