初识JVM
JVM特点概括
- 一个能够运行字节码的虚拟机
- 屏蔽了具体的操作系统的信息
- 一次编译,到处执行
JVM自动内存管理包括:运行时数据区域、垃圾收集算法、对象的创建与访问、HotSpot的算法细节、经典的垃圾收集器。本文主要介绍运行时数据区域
运行时数据区域
运行时数据区域主要包括以下几个区域:
- 堆(heap) ;
- 方法区(Method Area);
- 虚拟机栈(VM Stack) ;
- 本地方法栈(Native Method Stack);
- 程序计数器(Program Counter Register);
如图:
注:红色的是线程共享区域,绿色的是线程私有区域
堆(heap)
特点:
- 存储的是new的队形,不存放基本类型和对象引用
- 是垃圾回收器主要工作的区域
- 线程共享区域,所以本区域线程不安全
- 能够发生内存溢出,主要有OutOfMemoryError和StackOverflowError两种异常
OutOfMemoryError: 虚拟机在扩展栈时无法申请到足够的内存空间抛出
StackOverflowError: 线程请求的栈深度过深, 超过虚拟机所允许的最大深度
其他:堆中还可以分为新生代和老年代
虚拟机栈(VM Stack)
特点:
- 线程私有区域, 每一个线程都独享一个虚拟机栈,因此这是线程安全的区域
- 存放基本数据类型以及对象的引用
- 每个方法执行的时候都会在虚拟机栈中创建一个相应栈帧,方法执行完毕后该栈帧就会销毁。方法栈帧是以先进后出的方式虚拟机栈的。
- 每一个栈帧又可以划分为局部变量、操作数栈、动态链接、方法出口以及额外的附加信息
- 也会有两种异常OutOfMemoryError和StackOverflowError(StackOverflowError:一般是递归导致)
本地方法栈(Native Method Stack)
特点:本地方法栈是Java程序在调用本地方法的时候创建栈帧的地方
方法区(Method Area)
特点:
- 线程共享区域,因此这也是线程不安全的区域
- 方法去也是可能会发生OutOfMemoryError的区域
- 方法去存储的是从Class文件加载进来的静态变量、类信息、常量池、以及编译器编译后的代码
常量池可以分为Class文件常量池以及运行时常量池,Java程序运行后,Class文件中的信息被字节码执行引擎加载到了方法区,从而形成了常量池
说起方法区,有人把它与永久代、源空间混为一谈。其实他们的区别是:
方法区是Java虚拟机规范中的定义,是一种规范,而永久代是一种实现。一个是标准一个是实现。不过Java8以后就没有永久代这个说法了,元空间取代了永久代。
程序计数器(Program Counter Register)
特点:
- 线程私有,每一个线程都有一个程序计算器,因此它是线程安全的。
- 唯一一块不存在OutOfMemoryError的区域
场景:
- 某一次,线程A获得CPU的执行权,开始执行内部程序。但是线程A的程序还没有执行完,在某一时刻CPU的执行权被另一个线程B抢走了。后来经过线程A的不懈努力,又抢回了CPU的执行权,那么线程A的程序又要从头开始执行?
程序计数器记录当前线程所执行的位置,这样当A抢到了CPU的执行权的时候,就直接从记录的位置开始执行,分支、循环、跳转、异常处理也都依赖这个程序计数器来完成。