JVM自动内存管理
一、JAVA内存区与内存溢出
1.1 概述
java程序员在JVM自动内存管理机制的条件下,不需要为每一个new的操作去写配对的delete/free代码,虽然这样可以省去很多麻烦,但是正是因为我们吧对象的创建交给JVM去管理,所以一旦出现内存泄漏和溢出的问题,如果我们对jvm了解的不深入那我们在修正这些问题是会遇到很多麻烦
1.2 运行时数据区
上一张大家都见过的图
1.2.1 程序计数器 (Program Counter Register)
- 程序计数器是一块占用内存比较小的空间,可以看成是当前线程所执行的字节码的行号指示器(可以用电梯的指示楼层号信号灯来比喻),字节码解释器工作时就是通过改变当前程序计数器的值来选取下一条要执行的字节码指令,程序计数器是程序控制流的指示器,
分支、循环、跳转、异常处理、线程恢复等基础功能都是依赖程序计数器来完成的。Java虚拟机的多线程是通过线程轮流切换、分配处理器的执行时间来实现的,在任意一个确定的时间内 一个处理器只会执行一个线程(多核处理器理解成为一个内核)中的指令,因此为了线程切换后能恢复到正确的执行位置,所以每个线程都有一个独立的程序计数器,每个线程的计数器互不影响,独立内存。所以也被称为线程私有的
1.2.2 Java虚拟机栈(Java Virtual Machine Stack)
- 与程序计数器一样 Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java的方法执行的线程模型:每个方法被执行的时候都会同步的创建一个栈帧用于储存局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从被调用到执行结束的过程,也就是栈帧从入栈到出栈的过程
- 很多程序员都把JVM的内存笼统的归结为 “堆内存”和“栈内存”,其实本质上JVN的内存区域比这个笼统的说法要复杂很多,这也说明了大家对 “堆内存”和“栈内存”非常感兴趣~~ 其实“栈”通常就是指的Java虚拟机栈,但是更多的情况下是指的Java虚拟机中的局部变量表的部分
- 局部变量表中存放了可知的JAVA 基础数据类型 (boolean 、byte 、char、 short、int、float、long、double)、对象引用(reference类型,它可能并不等同于对象本身,可能是指向对象原始地址的引用指针等)
- 在Java虚拟机中有两类异常情况
- StackOverFolwError: 当线程请求的栈的深度大于虚拟机允许的最大深度时
- OutOfMemoryError: 如果虚拟机的栈容量可以进行动态扩展,当虚拟机申请不到足够的栈内存时,会抛出异常。俗称OOM
1.2.3 本地方法栈 (Native Method Stacks)
本地方法栈和Java虚拟机栈的作用非常相似,只不过Java虚拟机栈是为JAVA方法(字节码)进行服务,而本地方法栈则是为虚拟机使用到的本地方法进行服务,值得注意的是在我们常用的Hot-Spot虚拟机直接把本地方法栈和Java虚拟机栈合二为一了,所以在Hot-Spot虚拟机中当本地方法栈无法申请到内存时,也会抛出StackOverFolwError、OutOfMemoryError这两个异常
1.2.4 Java堆 (Java Heap)
Java堆是jvm管理的最大一片内存区域,Java堆是被所有线程共享的一块内存区域,虚拟机启动时堆内存就被创建,堆内存被创建的唯一原因就是存放内存实例 在Java 中几乎所有的的内存都在这里分配内存,这里提到了几乎一词,在Java虚拟机规范中对堆的描述是:“所有对象的实例和数组都应当在堆上分配”,但是随着Java语言的发展,即时编译技术的发展等,所以java 实例都在堆上分配也就没有那么绝对了。
1.2.5 方法区(Method Area)
- 方法区和Java堆是一样的 也是线程共享的一块内存区域它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据 虽然JVM虚拟机规范中吧方法区表述为堆的一个逻辑区域,但是他有一个别名叫做非堆,目的是与Java堆区分开。
- 这里应该提到一下永久代的概念 JDK8之前许多程序员喜欢把