Java虚拟机内存模型及回收机制,只需呀一篇文章就能搞懂!

200 阅读8分钟

1.平台无关性

(1)为什么要先编译成java字节码再解析成机器码

a.准备工作:每次执行都需要各种检查。

因为编译的过程本来就是在进行编译以及检查的过程,如果直接对java代码解析成机器码,岂不是每次解析的时候都需要进行检查,何不提前检查好了,再进行解析

b.兼容性:也可以将别的语言解析成字节码

比如Clojure,Groovy, JRuby, Jython , Scala等他们最终会 通过不同的编译器(java是javac编译器)编译成 .class字节码在虚拟机进行运行

(2)JVM如何加载.class文件

java虚拟机:

a.JVM内存结构模型

JVM是内存中的虚拟机,可以理解为,JVM的存储就是内存,我们所有写的常量,变量,方法都在内存中

JVM架构:

Java虚拟机内存模型及回收机制

ClassLoader(类加载器): 依据特定的格式,加载class文件到内存

Runtime Data Area(运行数据区域):JVM内存空间结构模型

Execution Engine (执行引擎):对命令进行解析(解释器,对命令进行解析,能不能运行需要他来负责)

Native Interface(本机接口):融合不同开发语言的原生库为java所用,使用Class.forName()方法中的forName0()方法进行其他语言的类库的调用,返回值是native

类从编译到执行的过程(java从源码到运行共有三个阶段)

Java虚拟机内存模型及回收机制

编译器将*.java源文件编译为*.class字节码文件Source源码阶段

ClassLoader将字节码转换为JVM中的Class<对象名>对象(实质是byte数组)Class类对象阶段

JVM利用Class<对象名>对象实例化 * 对象加载到内存Runtime运行时阶段

ClassLoader

作用:

ClassLoader负责通过将Class文件的二进制数据流(使用io流进行读取的)装载进系统,然后交给java虚拟机进行连接,初始化操作(从系统外获取Class的二进制数据流)

种类:(Alibaba Dragonwell 是一款免费的 OpenJDK 发行版)

BootStrapClassLoader :C++编写,加载核心库java.*hg.openjdk.java.net/jdk8u/jdk8u…

ExtClassLoader :java编写,加载扩展库javax.* System.out.println(System.getProperty("java.ext.dirs"));

AppClassLoader :java编写,加载程序所在目录System.out.println(System.getProperty("java.class.path"));

自定义ClassLoader

Java虚拟机内存模型及回收机制

Java虚拟机内存模型及回收机制

双亲委派机制:

Java虚拟机内存模型及回收机制

1.自下向上*检查*类是否已经加载自定义ClassLoader==》AppClassLoader==》ExtClassLoader ==》BootStrapClassLoader

2.自上向下尝试*加载*类BootStrapClassLoader (jre/lib/rt.jar)==》ExtClassLoader(jre/lib/ext/*.jar) ==》AppClassLoader(classpath目录)==》自定义ClassLoader

小编总结:双亲委派机制:检查类是否加载的时候,顺序是自下而上的。如果是加载类的时候,顺序是自上而下的

有资料或文档需求的可以私信我哦(发送“资料”二字即可)

为什么要使用双亲委派机制:

1.避免多份同样字节码的加载(使用委托机制逐层去父类查找是否加载)

类的加载方式:(了解)

隐式加载:new (隐式调用类加载器将类加载带JVM)

显式加载:loadClass,forName等

loadClass和forName的区别:(了解)

Classloder.loadClass得到的class是没有链接的JDBCUtils.class.getClassLoader()//getResourceAsStream("druid.properties")

Class.forName得到的class是已经初始化完成的Class.forName("cn.itcast.utils.JDBCUtils")

JVM内存模型-JDK8:

1.程序计数器:

Java虚拟机内存模型及回收机制

作用:

1.当前线程所执行的字节码行号指示器(逻辑)

2.改变计数器的值来选取下一条需要执行的字节码指令

3.和线程是一对一的关系,即线程私有

4.对java方法技术,如果是Native方法,则计数器值为Undefined

5.不会发生内存泄漏

干什么用的:循环,跳转,异常处理,线程恢复 都是【程序计数器】通过【改变计数器的值】来实现的,命令指向的作用。

2.java堆(Heap):

对象实例分配区域

GC管理的主要区域

3.java虚拟机栈:

Java虚拟机内存模型及回收机制

1.java方法执行的内存模型,每个方法运行的时候都会创建一个栈帧【一个栈帧就相当于一个方法执行的时候,存在栈里面的状态】

2.包含多个栈帧,栈帧用来存储(局部变量表【局部变量存储的地方】,操作栈【进行运算的地方】,动态连接?????,返回地址)方法调用结束时,帧才会被销毁

局部变量表:包含方法执行过程中的所有变量

操作数栈:入栈,出栈,复制,交换,产生消费变量

递归为什么会引发java.lang.stackoverflowerror异常?

递归过深,栈帧数超出虚拟栈深度,方法调用层次过多

java.lang.OutOfMemoryError异常?

虚拟机栈开启过多,线程开启太多,导致内存不足以创建新的虚拟机栈

扩展:方法结束,栈帧会自动释放,栈的内存,不需要GC去回收

(jstack命令可以生成虚拟机当前时刻的线程快照,可以定位线程出现长时间停顿的原因,如线程间死锁,死循环,请求外部资源长时间的等待)

jmap

本地方法栈:

与虚拟机栈相似,只要作用于标注了native的方法

4.方法区:

方法区里面存储了类信息、静态变量、即时编译器编译后的代码(比如spring 使用IOC或者AOP创建bean时,或者使用cglib,反射的形式动态生成class信息等)等。

JDK1.7及以后,JVM已经将运行时常量池从方法区中移了出来,放到堆中

元空间(MetaSpace):【方法区的实现,可以理解为方法区是规定放什么东西,元空间是真正存放方法区规定东西的内存】

永久代:是HotSpot(虚拟机)的一种具体实现,实际指的就是方法区

元空间与永久代(PermGen)的区别

元空间使用本地内存,永久代使用的是JVM内存

元空间相比永久代的优势

1.字符串常量池存在永久代中,容易出现性能问题和内存溢出

2.类和方法的信息大小难易确定,给永久代的大小指定带来困难

3.永久代会为GC带来不必要的复杂性【不懂】

4.方便HotSpot与其他JVM如Jrockit的集成

扩展:

JVM三大性能调优参数 -Xms -Xmx -Xss的含义

命令演示:java -Xms128m -Xmx128m -Xss256k -jar ****.jar

-Xss:规定了每个线程虚拟机栈的大小,一般来说256k足够,此配置会影响此进程中并发线程数的大小

-Xms:堆的初始值 (如果堆的大小超出初始值的容量怎么办,会扩容到-Xmx配置的堆的内存最大值)

-Xmx:堆能达到的最大值,一般来说我们的初始值和最大值设置一样,以避免每次垃圾回收完成后JVM重新分配内存

java内存模型中堆和栈---内存分配策略(了解)

静态存储:编译时就能确定每个对象目标在运行时的存储空间需求

栈式存储:(动态存储)数据区需求,在编译时未知,运行时模块入口前确定

堆式存储:编译时或运行时,模块入口都无法确定,动态分配

java内存模型中堆和栈的联系:

Java虚拟机内存模型及回收机制

引用对象、数组时,栈里定义变量保存堆中目标的首地址

java内存模型中堆和栈的区别【重点】:

管理方式:栈自动释放,堆需要GC进行垃圾回收

空间大小:栈比堆小

碎片相关:栈产生的碎片远小于堆(内存不能及时释放产生的碎片,因为GC不是实时的)

分配方式:栈支持静态和动态分配,而堆仅支持动态分配

效率:栈的效率比堆高

2.GC【Garbage Collection】

对象被判定为垃圾的标准,没有被其他对象引用的情况下

怎么判定是否为垃圾的算法

a.引用计数算法(了解)

通过判断对象的引用数量来决定对象是否可以被回收

每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1

任何引用计数为0的对象实例可以被当做垃圾收集

优点:执行效率高,程序执行受影响小

缺点:无法检测出循环引用的情况,导致内存泄漏

b.可达性分析算法(我们现在使用的是这个算法)

通过判断对象的引用链(GC Root)是否可达,来决定对象是否可以被回收

Java虚拟机内存模型及回收机制

如果GC Root判断对象是可达对象,就会将此对象标记为蓝色

如果GC Root判断对象是不可达对象,就会将此对象标记为灰色,标记为灰色的对象就会被GC回收

哪些对象可以作为GC Root对象

1.栈中引用的局部变量【方法里面的对象】【方法执行会被引用】

2.方法区中类【静态】属性引用的对象【类中的对象】【类加载会被引用】

3.方法区中常量引用的对象【常量】【类加载会被引用】

4.本地方法栈中JNI(即一般说的Native方法)引用的对象【native方法的引用】

就讲到这里啦~

希望可以帮到你们

有资料或文档需求的可以私信我哦(发送“资料”二字即可)

小编都会尽全力给你们解答哒~

或直接关注公众号【乐字节】(关注后记得一定要回复小编的暗号66哦!)