深入理解JVM,及GC工作机制

话不多说,先上两张图,一张为中文版jvm内存结构图,一张为英文版的流程图

 

一、JVM内存分区

 

从JVM内存图中可以将其JVM大致分为四个区域:1.类加载器、2.内存区、3.执行引擎、4.本地库接口

 

那么我们分别根据图示来讲讲其完成了哪些功能:

1.类加载器(ClassLoader):在JVM启动时或者在类运行时将需要的class加载到JVM中,那么从英文版的内存流程图可以看出执行顺序。关于具体的类加载问题可以看另一篇文章(类加载器(ClassLoader)的解析);

 

2.内存区:是在jvm运行的时候操作时所分配的内存区。采用分治思想。运行时内存区主要可划分5个区域:

1.堆(Heap)、2.方法区(Method Area)、3.虚拟机栈(VM Stack)、4.本地方法栈(Native Method Stack)、5.程序计数器(Program Counter Register)

那么我们来详细讲解这几个内存区

1.堆(Heap):

存储Java实例或者对象的地方。此内存区是GC的主要区域。从存储的内存我们就可以很容易知道,方法区和堆内存区是被所有Java线程共享的。

2.方法区(Method Area):

用于存储类结构信息的内存区域,包括常量池、静态变量、构造函数等。虽然JVM规范的把方法区描述成为堆的一个逻辑部分,但是它却有个别名称non-heap(非堆内存区),所以大家对于两者注意区分。方法区还包含一个运行时常量池。

3.虚拟机栈(VM Stack):

Java栈总数和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的Java栈。在这个Java栈中又会包含多个栈帧,每次运行一个方法就会创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在Java栈中栈道出栈的过程。所以Java栈是线程私有的。

4.本地方法栈(Native Method Stack):

和虚拟机栈的作用差不多,只不过是为JVM使用到的native方法(本地方法)服务的。

5.程序计数器(PC Register):

用于保证当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来之后,还能保持恢复到原来的状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。

 

二、那么我们接下来讲一下内存分配:

至于为什么我把内存分配放在第二栏,是因为对于JVM垃圾回收机制的了解之前,应该需要了解一下内存是哪些区域回收的,最后使用的什么方式去回收的。这些都是前提。

Java内存分配原理和C有所不同,c每次申请内存时都要memory allocation(动态内存分配),而系统调用发生在内核空间,每次都要中断进行切换,这就需要一定的系统消耗。而Java虚拟机不同,它先一次性分配一块较大的空间,然后每次new时都在该空间上进行分配和释放,减少了系统的调用的次数,节省了一定的系统消耗,这和内存池机制有相似;第二点就是有了这块内存空间后,如何进行分配和回收就和GC 机制有相关了。

 

Java一般内存申请方式有两种:静态内存和动态内存。很容易理解,编译的时候就能够确定的内存称为静态内存。就内存固定,系统一次性分配,比如int类型变量;动态内存分配就是程序执行时才知道要分配的存储空间大小,比如Java对象的内存空间,根据上面我们知道,Java栈、程序计数器、本地方法栈都是线程私有的,线程生就是生,死就是死,栈中的栈帧随着方法的结束也会撤销,内存自然就跟着回收了。所以这几个区域的内存分配与回收是确定的,不需要程序员关系。但是Java堆和方法区则不一样,我们只有在程序运行期间才会知道会创建哪些对象,所以这部分内存的分配和回收都是动态的。一般我们所说的垃圾回收也是针对的这一部分。

总之栈内存的内存管理是顺序分配的,而且固定长度,不存在内存回收问题;而堆则是为Java对象的实例随机分配内存,不定长度,所以存在内存分配和回收的问题;

 

三、jvm垃圾检测算法、垃圾回收算法

四、jvm垃圾收集器

五、总结

 

posted @ 2019-02-26 22:36  CHANGEMAX  阅读(194)  评论(0编辑  收藏  举报