这是我参与「第四届青训营 」笔记创作活动的第5天。
今天在学习安卓知识的时候学习到了安卓虚拟机的相关,感觉和之前的JVM比较相关,故整理了一下JVM相关知识复习。
JVM作用
- 通过 ClassLoader 寻找和装载 class 文件
- 解释字节码成为指令并执行,提供 class 文件的运行环境
- 进行运行期间垃圾回收
- 提供与硬件交互的平台
私有和共享的资源
私有:
1.java虚拟机栈
2.程序计数器
3.本地方法栈
共享:
1.java堆 (在进程空间中,new出来的)
2.方法区
内存模型
可以分为3个区:堆(heap),栈(stack)和方法区(method)
堆区
-
存储对象实例,每个对象都包含一个与之对应的class信息(class存放在方法区)
-
共享:JVM只有一个堆被所有线程共享,只存放对象本身
-
范围:
- 几乎所有的对象实例和数组都在堆中分配
- 方法的局部变量
-
是JVM管理的内存最大的一块
方法区
-
共享:又叫静态区,被所有的线程共享。
-
它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码(字面量和符号引用)等数据
-
常量池
-
运行时常量池:
- 是每一个类或接口的常量池的运行时表示形式.
- 具体体现就是在Java编译后生成的.class文件中,会有class常量池,也就是静态的运行时常量池;
- 存放内容:存放编译期生成的各种字面量和符号引用;
-
字符串常量池:
- 在JVM中,为了减少相同的字符串的重复创建,节省内存。会单独开辟一块内存,用于保存字符串常量
-
栈区
生命周期和线程相同。虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(stack Frame)存储局部变量表、操作数栈、动态链表、方法出口等信息。 每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程
- 每个线程包含一个栈区
- 保存方法中的(不包括对象的成员变量)中的基础数据类型和自定义对象的引用(不是对象)
- 私有:栈中的数据(原始数据和对象引用)都是私有的,其他栈不能访问
- 栈分为3部分:基本变量区,执行环境上下文,操作指令区(存放操作指令)
程序计数器
线程共享
功能:可以看作是当前线程所执行的字节码的行号指示器,字节码解释器工作时就是通过改变整个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能。
每条线程都需要一个独立的程序计数器:由于JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,所以线程私有。
没有OOM错误:程序计数器是唯一一个在Java虚拟机规范中没有规定任何OOM(OutOfMemoryError)的区域。
本地方法栈
线程私有。
和虚拟机栈的区别就在于一个是为了执行Java方法服务,一个是为了虚拟机使用到的Native方法服务。
分区
JVM堆: 1. 新生代(通常一个Eden区[ 4/5空间],两个Survivor[1/5]) 2.老年代(old区,占2/3)
方法区:常量池
内存配置参数
-Xmx:最大堆大小
-Xms:初始堆大小
-Xmn:年轻代大小
-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值
例题
|-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3
年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个,即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。
\