学习JVM笔记
JDK Java开发工具,包含JRE 和JVM ;
JRE Java运行环境,包含JVM ;
JVM 处理Java编译好的字节码文件,执行代码,可处理任意平台,如windows、linux等等。
思考:JVM只能运行Java的.class(字节码)文件吗,
答案是否定的,其它语言只要也符合字节码文件的规则,JVM都是可以运行。
字节码文件的基本执行流程
class文件–>类加载器–>JVM内存(运行时数据区)–>执行引擎–>操作系统
JVM的内存管理
自动化管理,内存分配自动,垃圾回收自动。
举个例子:把class文件的执行比作我们吃东西的一个过程,那class文件就是食物,喉咙作为我们的类加载器来接收食物,我们的胃就是JVM内存,消化吸收,将营养分配到五脏六腑,到最后的排泄,就是一个垃圾自动回收。
运行时数据区
JVM在运行过程中会把它所管理的内存划分成为若干不同的数据区域!
线程私有:虚拟机栈、本地方法栈、程序计数器
程序共享:堆、方法区
栈(Stack):数据结构
入口和出口只有一个,先进后出(FILO)。
模型可以想象成一个杯子,而先放进去的,只能后被拿出来,后放进去的,可以先拿出来。(不要杠精附体,比如沙子等等)
线程私有
1、程序计数器
指向当前线程正在执行的字节码指令的地址(行号),会记录其他线程的字节码指令的地址(行号),
思考:为什么需要程序计数器
1、因为Java是多线程的语言,意味着我们会线程切换,如果没有程序计数器,当我在线程来回切换的过程中,我不知道我刚刚执行到哪里了;
2、确保多线程情况下的程序能正常执行,不会被来回切换而错乱。
2.运行时数据区-虚拟机栈
存储当前线程运行方法所需的数据、指令、返回地址等
2.1栈帧
类中每一个方法对应一个栈帧
栈帧还可以划分为:
局部变量表:第0号位置,指向的是this,也就是类对象的本身,其它位置,指向定义的参数,其他位置,依次是定义的变量;
操作数栈:定义的变量在需要发生变化,在此栈中操作;
动态连接:执行的时候才知道指向哪里,它指向的是我们的引用链接;
返回地址:return语句的执行。
javap -v 类名.class 可以将字节码文件反编译。
3.本地方法栈
为虚拟机执行Native方法服务。
线程共享(数据)/JMM:JVM的内存模型
堆:存放对象,成员变量等,在JMM中表现为新生代和老年代;
新生代还可以分为:
Eden空间,
From空间,
To空间,
新生代一般占据堆内存的三分之一内存;
老年代:一般占据堆内存的三分之二内存;
元空间:在jdk1.8及其以上叫元空间,在jdk及其以下,叫老年代。
方法区:存放静态变量,final常量等,在JMM中表现为元空间。
对象分配原则:
1、对象优先在Eden分配,
2、大对象直接进入老年代,
3、长期存活的对象将进入老年代,
4、动态对象年龄判定,
5、空间分配担保。
在新生代中,Eden:From:To 是8:1:1的原则进行内存分配;
假设堆内存分配到300M内存,那么新生代就会得到100M内存,老年代得到200M内存,那么自然的,Eden、From、To就分别为80M,10M,10M的空间,当我们在Eden空间中存放了79.5M的对象,此时再来1M的对象,就会触发GC垃圾回收,没有用的就会被回收掉,有用的会进入From空间,并对此对象进行一个年龄计算,每回收一次,年龄加1,默认年龄为15之后,进入老年代。
如果From空间或者To空间中存放对象的内存超过本身内存的百分之五十,就会有一个动态年龄的判断,触发垃圾回收算法(MinorGC),提前晋级老年代。
在老年代中,如果我们存放的对象快要将内存占满,此时新生代中有对象要进入老年代,但是老年代的空间无法满足存储这个对象,就会触发垃圾回收算法(FullGC),对之前的对象进行回收,来确保我们新生代对象能放入老年代。
JMM–对象的回收——判断对象的存活
假如在程序中,定义了一个常量,此常量对象在老年代中,当常量在程序中被引用,那么就认为此常量是可达的,反之,则为不可达。