JVM(一)内存区域与内存溢出异常
文章目录
1. JVM简介
虚拟机简介 :JVM(Java Virtual Machine的简称,意为Java虚拟机)
虚拟机 :指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统
常见的虚拟机 :JVM、VMwave、Virtual Box
Docker : (大白话来讲) docker是用来跑应用的一种容器
参考文章
2. JVM内存区域划分
JVM内存区域划分 ---- 6大区域
线程私有内存
程序计数器、虚拟机栈、本地方法栈
什么是线程私有?
由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现,因此在任何一个确定的时刻,一个处理器(多核处理器则指的是一个内核)都只会执行一条线程中的指令。因此为了切换线程后能恢复到正确的执行位置,每条线程都需要独立的程序计数器,各条线程之间计数器互不影响,独立存储。我们就把类似这类区域称之为"线程私有"的内存。
线程私有 :
- 程序计数器、虚拟机栈、本地方法栈的生命周期与线程的生命周期完全相同;
- 随着线程的创建而创建,随着线程的销毁而回收.
- 不同线程这三块内存彼此隔离.
I. 程序计数器 :
程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的程序的行号指示器
程序计数器内存区是JVM中唯一 一个没有规定任何OOM(Out-Of-MemoryError)异常的区域!
- 如果当前线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;
- 若执行的是
native
方法,计数器值为0
我们来回顾一下CPU对中断的响应 :
大二上学期学的微机原理,早忘光了
这里的程序计数器就类似于保留现场、保护断点这一操作,当CPU响应中断时,会把断点记录在一个寄存器中,转而去响应中断. 而在JVM中,程序计数器充当了这一角色,我们就把程序计数器存放数据的这一区域称之为"线程私有"内存.
II. 虚拟机栈 :
虚拟机栈描述的是Java 方法执行的内存模型
-
每个方法执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息
-
每一个方法从调用到执行完成的过程,就对应一个栈帧在虚拟机栈中入栈和出栈的过程
局部变量表 : 存放了编译器可知的各种基本数据类型(8大基本数据类型)、对象引用
- -Xss设置栈容量
此区域一共会产生以下两种异常 :
- 如果线程请求的栈深度大于虚拟机所允许的深度(-Xss设置栈容量),将会抛出
StackOverFlowError
异常 - 虚拟机在动态扩容时无法申请到足够的内存,会抛出
OOM(OutOfMemoryError)
异常
OOM产生的原因:
内存泄露 :参生的对象永远无法被垃圾回收器回收
内存溢出 :当前堆的空间过小,没有足够空间容纳新的对象(适当的将堆的大小适当扩大)
III. 本地方法栈
native
方法的内存模型
HotSpot : (Java默认的虚拟机) 本地方法栈与虚拟机栈合二为一
线程共享内存
线程共享 : 所有线程共享Java堆、方法区、运行时常量池这三块内存,彼此不隔离
I. Java堆
- Java堆(Java Heap)是JVM所管理的最大内存区域,所有的对象实例以及数组都要在堆上分配
- Java堆是垃圾回收器的主要区域(GC堆),Java堆可以处在物理上不连续的内存空间中
- Java堆在虚拟机中都是可扩展的(-Xmx设置堆的最大值,-Xms设置最小值)
- 如果在堆中没有足够的内存完成实例分配并且堆也无法再拓展时,将会抛出OOM异常
II. 方法区
- 存储 被JVM加载的类信息、常量、静态变量、编译后的代码等.
- 方法区也被称为"永久代"(JDK8已经被元空间取代)
III. 运行时常量池
运行时常量池是方法区的一部分,存放字面量与符号引用
字面量 : 字符串、final常量、基本数据类型的值(直接写出来的值)
符号引用 : 拿着符号引用能够找到具体的类或方法
下一节 : JVM(二)垃圾回收器与内存分配策略