JVM---内存管理
一、介绍:
JVM 是用于识别.class ,jar等字节码文件,将字节码翻译成操作系统可以识别的机器码的虚拟机,主要充当的是翻译的工作。
特点:跨平台,跨语言的。
JRE:提供了我们开发所需要的基础类库,也就是允许所需要的基本类库
JDK:提供了一套工具集(如javac ,javap,javah ,javaw等工具)
二、JVM加载字节码文件的运行过程:

1、运行时数据区
运行时数据区主要包括一下内容:方法区、堆、虚拟机栈,本地方法栈、程序计数器 五部分的内容
1.1 程序计数器
由于操作系统的时间片轮转机制,程序线程会在运行过程中进行切换轮流执行,这时候程序计数器就是保存字节码在程序线程运行执行的偏移量地址(也就是在字节码文件签名的序号)
说白了,就是在操作系统切换线程执行时,保存当前线程执行的位置,方便下次恢复执行时可以继续执行
程序计数器是唯一不会发生OOM的内存区。
1.2 虚拟机栈
存储当前线程运行方法所需要的数据,指令,返回地址等信息。虚拟机栈大约是1M大小。
执行顺序:先进后出
虚拟机栈就像是一个子弹夹,而线程的方法就像是每一颗子弹,在运行过程中不停的进栈出栈,完成方法的执行。
一个方法就是一个栈帧。
栈帧包含如下内容:
**A、局部变量表:主要存放基础数据类型** **B、操作数栈:存放方式的执行,代码的运行, java的解释执行就是基于操作数栈的** **C、动态链接:主要是多态的支持,动态静态分派** **D、完成出口:正常返回程序计数器中的地址**
1.3 本地方法栈:用于保存C/C++实现的本地native的方法
1.4 方法区(元空间)
主要保存类的信息,常量,静态变量,编译期编译生成的代码,该区域比较难回收。
1.5 堆
用于存放java的对象实例,数组等内容,该区域的对象回被频繁的回收
2、 直接内存:
不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域,NIO会使用直接内存,不受java堆大小的限制
3、运行时内存关系图:

三、JVM允许Java类的内存步骤:
1)申请内存
2)类加载---将.class放入到方法区
3)将常量,静态变量放入方法区
4)虚拟机栈:将方法加入栈帧,将new出来的对象引用加入到局部变量表中
5)入堆:将new出来的对象加入到堆中
四、内存溢出的场景分析:
常见的出现内存溢出的场景有:
1)栈溢出:
例如方法只入栈,不出栈,类似一个永久递归,就会导致栈溢出
2)堆溢出:
申请内存时,没有需要大小的内存可用时,会发生堆溢出
3)方法区溢出
4)本地直接内存溢出
五、虚拟机优化技术:
针对内存溢出的场景,下面有两种比较常见的虚拟机优化技术:
6.1)方法内联:
减少内联方法的入栈,减少栈帧(简单的方法减少调用)
6.2)栈帧之间数据共享:
上一个栈帧的局部变量表与下一个栈帧的操作数栈质检的数据共享,(通过参数传递来共享)
六、堆和栈辨析:
1)栈:
存放方法的调用过程
存储基本数据类型、对象的引用变量、变量除了作用域会自动释放
栈内存属于单个线程,每个线程都有一个栈内存,栈内存可以理解成是线程的私有内存
栈内存大小小于堆内存,可能发生栈溢出问题
2) 堆:
存放java中的对象,无论是成员变量、局部变量,类变量,他们指向的对象都存储在对内存中
堆内存中的对象对所有的线程都是可见的,可以被所有线程进行访问
关于JVM内存管理的一些知识点就先介绍到这里,下一节我们着重讲一下虚拟机是如何管理对象的。