JVM基本常识了解
1,JVM : Java Virtual Machine java虚拟机,实现跨平台工作的根本原因2,JRE:Java Runtime Environment :Java运行时环境整体来说属于运行时阶段JVM分成三大块:类加载模块,内存管理模块,执行引擎模块。JVM两个版本:1,hotspot(商业)2,openjdk(开源)软件执行的技术支持:硬件的支持:CPU,内存,IO设备核心:CPU时无法
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的各种算法,各有利弊,
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)