转载

Java基础篇之Java虚拟机(一)------内存模型和结构; 垃圾回收机制;类加载机制

此次记录内容主要内容包括以下三块内容:

内存模型和结构;

垃圾回收机制;

类加载机制;

一,Java内存模型与JVM内存结构

JVM内存模型与内存结构是两个截然不同的东西,大家都知道Java内存分为堆和栈,被问到内存相关都会这样回答,但是今天要弄清楚什么是内存模型什么是内存结构?

JVM内存模型简称(JMM)是一种抽象的概念,并不是像JVM内存结构一样是实际存在的,而是因为Java的多线程之间是通过共享内存进行通信的,而由于采用共享内存进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM就是围绕着多线程通信以及与其相关的一系列特性而设计建立出来的的模型。

目的是解决由于多线程通过共享内存进行通信时候,存在本地内存数据不一致,,编译器对代码重排序,以及处理器对代码乱序执行带来的问题,从而保证Java程序在各种平台下对内存的访问都能保证效果的一致性。

JVM内存结构是一种实际存在的结构体,由Java虚拟机规范定义,是Java程序执行过程中,由JVM管理的不同数据区域,并且各个区域有其特定的功能。

Java基础篇之Java虚拟机(一)------内存模型和结构; 垃圾回收机制;类加载机制

 二,JVM垃圾回收机制  

Java语言一般情况下不需要开发人员自己去处理内存问题,本身存在垃圾回收机制,那么Java的垃圾回收机制到底是什么?

在说明垃圾回收机制之前先说一下Java中的内存溢出以及内存泄漏的是怎么回事?

内存溢出:out of memory,是指在程序申请内存时候,没有足够的空间可供提供,比如加载一个大型游戏出现内存不足,再简单一点就是在仅仅只有Integer大小的空间内放置Long大小的数据,那么就会造成内存溢出。

内存泄漏:memory leak,是指程序申请到了内存,但是程序没有在不需要内存空间时候即使释放,造成内存空间只会一直减少,这种行为被称为内存泄露,内存泄漏一次没有关系,但是多次的内存泄漏积累的结果一定会造成内存不足。

好的我们开始从以下几点进行逐步了解Java主流虚拟机hotspot的垃圾回收机制,其他类型的虚拟机感兴趣的可以去做更深入了解。

1,什么是垃圾?

没有被引用的对象在Java中就被称为垃圾

2,怎么判断垃圾?

判断一个对象是否成为垃圾有以下几种方式

引用计数法 :

为对象添加一个引用计数器,当对象增加一个引用时计数器就加1,引用失效计数器就减1,当引用技术为0时对象可被回收。不过,当两个对象出现循环引用时,两个对象的引用计数器永远不会为0,这样会导致他们无法被回收。

引用可达法

以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。

GCRoot一般为以下几种:

  •  虚拟机栈中局部变量表中引用的对象 
  •  本地方法栈中 JNI 中引用的对象 
  •  方法区中类静态属性引用的对象 
  •  方法区中的常量引用的对象 

方法区回收

方法区存的一般就是永久代(年老代)对象,回收起来比年轻代要更加困难,所以对方法区的回收条件更加严格,主要也是对常量池的回收和类的卸载,满足以下条件才有可能被回收,但是也不一定会被回收。

该类所有的实例都已经被回收;

此时堆中不存在该类的任何实例;

加载该类的 ClassLoader 已经被回收 ;

该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。

finalize()

finalize()是Object的方法,存在的意义看似是,当类复写了finalize()方法,可以在对象被回收之前只调一次,来达到所谓的完成最后的功能,但是即使调用了,也不能保证方法会全部执行,可能会在方法执行过程中被回收

3,垃圾怎么回收 ?

标记清除算法

分为两个阶段,先标记出来要回收的区域,然后同意进行清除。

Java基础篇之Java虚拟机(一)------内存模型和结构; 垃圾回收机制;类加载机制

存在问题:

1,内存不连续,空间碎片太多

2,标记和清除效率低

复制算法(新生代)

将可用内存分为两等份,每次使用其中一份,进行垃圾回收的时候将,存活对象复制到另一块没有使用的可用空间上面,然后把使用过的内存空间一次性全部清理。

优点:简单高效,内存连续

不足:内存利用不充分,当对象存活率普遍较高时,由于内存只是用一半,需要多次执行。

Java基础篇之Java虚拟机(一)------内存模型和结构; 垃圾回收机制;类加载机制

标记整理算法(老年代)

标记整理,与上面的标记清理类似,只是标记整理不是将被标记的进行清除,而是将存活对象移动到一起,将剩余外界的进行清理。

Java基础篇之Java虚拟机(一)------内存模型和结构; 垃圾回收机制;类加载机制

分代收集算法

Java会把Java堆区域分为新生和年老代,然后根据不同的年代进行不同算法的回收。

新生代:大批会被回收,只有少量会继续存活,所以复制算法更加合适。

老年代:被回收的几率较低,所以适合标记整理,标记清除算法。

4,Java不同类型的引用

强引用:

Object obj = new Object()
复制代码

普遍存在的引用,只要引用还在就永远不会被回收。

软引用:

软引用用来描述一些还有用但是非必须的对象,系统在将要发生内存溢出之前,将会把这些对象列进回收范围,并进行第二次回收,如果将弱引用对象及进行回收之后依然没有足够的内存,那么会抛出内存异常。

弱引用 :

非必须对象的引用,强度要比软引用更弱,被弱引用指向的对象只能生存到下一次垃圾回收之前。当垃圾回收器工作时候,无论内存是否足够,都会回收掉被弱引用指向的对象。

虚引用 :

虚引用存在不存在没有太大意义,仅仅是为了这个被虚引用指向的对象在被垃圾回收器回收的时候收到一个系统通知。

 三,类加载机制 

什么是类加载机制?

Class文件中的各种信息,都是需要加载到虚拟机中才能运行,虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机使用的Java类型,这就是Java虚拟机的类加载机制。

类加载过程:加载-->验证-->准备-->解析-->初始化-->使用-->卸载

双亲委派模型是什么?

双亲委派模型(Parents Delegation Model)要求除了顶层的启动类加载器,其余加载器都应当有自己的父类加载器,类加载器之间通过组合关系复用。 双亲委派模型工作过程:如果一个类加载器收到类加载请求,他不会自己去加载这个类,而是请求给父类加载器去完成,每个层次的加载器都是如此,因此所有的加载任务都会传到顶层的启动加载类,只有在父类反馈无法完成加载任务的时候,才会由子类自己去完成加载工作。

为什么要有双亲委派模型?

采用双亲委派模型使得Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。

记录这些也是记录自己学习过程,所以如果您发现不足或者错误之处,敬请雅正、不舍赐教。

如果你也正在学习或者复习中不妨关注微信公众号:【Java成长录】一起学习。

Java基础篇之Java虚拟机(一)------内存模型和结构; 垃圾回收机制;类加载机制

参考文献:

https://www.hollischuang.com/archives/2509)

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5(Java虚拟机规范)

周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011.

原文  https://juejin.im/post/5dd741eb51882572ff406378
正文到此结束
Loading...