1,JVM : Java Virtual Machine java虚拟机,实现跨平台工作的根本原因

2,JRE:Java Runtime Environment :Java运行时环境

整体来说属于运行时阶段

JVM分成三大块:

类加载模块,内存管理模块,执行引擎模块。

JVM两个版本:1,hotspot(商业)    2,openjdk(开源)

软件执行的技术支持:

硬件的支持:

CPU,内存,IO设备

核心:CPU时无法和设备之间直接打交道,只能和内存打交道。

开发人员开发的程序,是保存在硬盘上的。所以开发的程序是无法直接和CPU进行交互的。

为了解决这个问题,JVM的类加载子模块,会将数据从硬盘加载到内存中,解决这个问题。

类加载器(ClassLoader):

输入:一个类名称

输出:加载到内存中的类数据。

1,使用类加载的时机:

1),程序开始启动时,就将程序需要的所有的类加载到内存中。(启动慢,运行快,占内存)

2),程序在开始执行时,不加载,只有在需要使用的时候,才会加载到内存中。(启动快,运行慢,不需要太大的内存)

2,什么叫一个类被使用:

需要使用类的对象(new,和class对象),类的属性,类的静态方法,类的父类(接口)就叫类被使用。

3,类的名称:包名+类名

4,硬盘上的文件(文件系统树),类加载器是如何知道去哪里寻找这个类文件?

针对jdk提供的类:加载类的路径是绑定在JVM程序路径上的,由核心类加载器加载

针对应用类:启动的时候,通知JVM去哪里寻找类,通常写作 -classpath : 类的绝对路径,将需要的类的绝对路径加载到classpath中,第三方类也是保存在磁盘中,

5,JVM中针对不同的类,(默认情况下)设计了不同的类加载器

1),启动类加载器:负责加载常见的jdk提供的类,

2),应用类加载器:负责加载开发人员写的类和引入的第三方的类

3),扩展类加载器:负责加载jdk提供的不常见的类

6,类加载器可以加载类文件,类文件中保存了什么,是以什么结构进行组织的?

保存了:类的名字,父类,接口个数,哪些接口,类的常量池,属性个数,属性信息数组(以上是使用数据形式保存),类的方法信息(使用字节码保存)

 7,类加载过程中可以再次进行划分:

加载,1)验证类数据是否符合规范,2)文件的读操作,解析 3)将读取到的内容组织成一个逻辑整体‘类’,放在内存中

链接,:类与类之间存在关系,链接就是将加载到内存中的的类之间产生关系。

初始化:类的基本信息初始化,涉及:类的那些指令要被执行,执行的顺序是什么样的。

8,类的加载过程中,初始化的几个时机,按照什么顺序

静态属性               在类加载的时候执行一次

static{。。。}        在类加载的时候执行一次

加载顺序:按照代码的书写顺序加载

父类加载完成加载子类

9,类被加载到那个区域:方法区(字节码文件肯定放在这里区域)

10,类有没有可能会卸载(将类从内存中移除): 一个类没有被使用就会被卸载

11,

JVM如何确定唯一的类:累加器 + 类名

JVM支持自定义类加载(继承ClassLoad类,重写一些方法):Tomcat里面实现了自己的类加载器

双亲委派模型:

双亲:parent,只有一个(不是继承)

双亲委派:应用类加载器被指定加载一个类之前,应该先交给自己的双亲进行加载,如果双亲可以加载,使用双亲加载,否则自己再去加载。

执行引擎:类比成硬件中的cpu

JVM这里的执行引擎何线程是绑定的:JVM中的线程,类比cpu中的一个核,每个线程都有自己的pc:类比每个核都有自己的程序计数器。

当一个Java程序启动 时:

JVM内存管理模块GC

GC含义1:垃圾回收器,负责进行内存管理模块的组件

含义2:一次垃圾回收的过程

执行引擎在什么时候会用到内存:

1,实例化对象时(存数据),2,类加载时(存类信息),  3,当一个方法被执行时(局部变量) 4,常见一个线程时。

JVM的内存划分

1,方法区,                                        存放指令方法的

2,堆区,                                            存放对象

3,Java调用栈、本地方法调用栈        存储方法调用栈,局部变量

4,运行时常量池                                  存放类文件中的常量

5,pc                                                    每个线程独有的,存放下一条指令地址

划分区域的好处:

因为功能的区别,划分区域,可以方便的进行内存管理,更方便的做出不同的处理方式,

这几个区域那些是线程共享的,那些是私有的:

共享:堆区,方法区,运行时常量池

私有:方法栈,pc

PC在什么情况下,会分配内存?

      线程在被创建的时候

运行时常量池在什么情况下,会分配内存?

    类加载时

堆在什么情况下,会分配内存?

    对象在创建时

方法栈帧在什么情况下,会分配内存?

    方法被调用时

方法区在什么情况下,会分配内存?

   类加载时

PC在什么情况下,会回收内存?

      线程在被终止的时候

运行时常量池在什么情况下,会回收内存?

    类被卸载时

堆在什么情况下,会回收内存?

    对象在不在使用时

方法栈帧在什么情况下,会回收内存?

    方法调用结束时

方法区在什么情况下,会回收内存?

   类卸载时

明确回收的时机:

1,PC(线程被销毁),方法栈(方法执行结束)

方法区,运行时常量池,对象不在使用很难明确

关于堆上的垃圾回收

堆上的内存都是以对象管理的,所以堆的垃圾回收,都是回收对象

1,如何判定垃圾对象(那些对象不被使用)

靠引用的持有情况,如果一个对象的所有引用时都不被应用持有,那么就被判定为不在使用(理想情况下),事实上是做不到的,所以采用了退而求其次的做法,保证回收的都是垃圾对象,不保证所有的垃圾对象都被回收。

垃圾判断算法:

1,引用计数法:JVM中没有这么使用,但在PHP,C++的智能指针是这么实现的。

实现思想:给对象设计一个引用树refCount,维护对象被引用指向的次数

当首次new对象,引用之间赋值时,refCount++;

当引用变量出了作用域,静态属性,类被卸载时,refCOunt--;

当refCount == 0 时,就一定不会被用到,就可以判定为垃圾对象。

引用计数法存在着一个问题,是本身无法解决的:循环引用,

2,Hotspot采用可达性分析法

JVM管理的对象一定是使用图形进行管理起来的,

 

 我们将此(程序的出发点所包含的引用)看作为GC的根节点 GC Roots

GC在垃圾回收时,对象的可达结构不变,判断是否可以到达堆上的对象,如果不能够到达,就会判定为来及对象,等待回收。

GC Roots包含:所有的静态属性,当前线程中的栈帧所持有的引用。

引用的分类:

引用本身定义出来是通过引用找到对象,但是GC的出现,引用可以决定对象的生存,

所以按照对象回收的优先级,将引用划分成四个等级:

1,强引用:通过引用可以找到对象,对象不能被回收

2,软引用:通过引用找到对象,对象的回收优先级低,可以被回收(内存不够用时)

3,弱引用:通过引用找到对象,对象的回收优先级较低,可以回收(内存不共用时)

4,虚引用:仅在对象被回收时收到通知。

2,如何进行垃圾回收(垃圾回收算法)

GC在在每次为对资源的释放何分配,可以类比成一个线性表,分配资源是,将线性表的空间分配给对象,释放资源时,将线性表的空间释放。

这种方式会存在内存碎片的问题

存在三个空间,但是只能分配两个连续的空间,

解决方法1:整理+回收

将资源回收在整理,将内存复制删除后,使内存连续,这种方法,分配资源的的时间复杂度为O(1),回收的时间复杂度为O(n);

 

解决方法二: 分代算法

一个应用中创建的对象,大部分是短生命周期的对象 ,90%活不过一个GC

复制+回收算法:

 垃圾回收的分代流程:

 

 

3,什么情况进行垃圾回收

1,内存不够或者达到了空间阈值,就进行垃圾回收,GC不能够太频繁,也不能太频繁,因为GC的代价很大。

2,定期进行GC

FUll  GC (整个清理) = MInor GC(新生代GC)  + Major GC(老年代GC)

大部分GC,都是Minor  GC (新生代GC) ,所以耗时不高,但随着Minor GC,不断有新生代对象,进入老年代,老年代会触发阈值,导致老年代GC ,所以Major GC 一般都是会导致 FUll GC。

GC的各种算法,各有利弊,

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐