一、为什么要学习Java虚拟机?
这儿我们使用举例来说明为何要学习Java虚拟机,虽然这个问题就和为何要学习数据结构和算法是一个道理,工欲善其事,必先利其器。以前的我常常担心处理显存溢出的问题,由于不晓得他为何会出现这个问题,当我在看了这本书之后明白了垃圾回收算法,以及JVM是怎样帮助我们处理GC的,这个时侯当出现这个问题的时侯我就明白须要查找GCRoot,或则查看GC日志,去查找这个问题的症结,这样才能处理那些问题。还有先前的在理解重载和构建的时侯只是在表面去理解,当我看完这本书明白,原先在方式调用时侯那些东西就生成处理,另外还有一个new究竟经历这些事情等等一序列问题,假如你还在就苦恼一些问题为何是如此处理的时侯那你就去看Java虚拟机吧,毕竟会有不一样的感受,以上就是为何要学习Java虚拟机的缘由,可能有部份解释的不是很全面,我想在感受方面在仔细说一下这个问题。
二、感悟?
虽然也算不上哪些感受,只是对一些问题认识愈发深刻而已,这儿面我们来谈一下GC,要阐述这个问题我们须要从4个方面入手:
1.JVM是怎样分配显存的?
2.怎么能够保证正确的回收?
3.JVM哪些情况下触发GC以及GC的形式?
4.怎么监控和优化GC?
首先从JVM显存分布开始:右图是JVM显存分布图
1.线程计数器,是一块较小的显存空间,拿来指定当前线程执行字节码的行数,每位线程计数器都是私有的,由于每位线程都须要记录执行的行数;这儿解释一下为何每位线程都须要一个线程计数器,JVM的多线程是通过线程轮流切换分配执行时间来实现的,在任何时刻,每位处理器都只会执行一个线程中的指令,当线程进行切换的时,为了线程能恢复当正确的位置,所以每位线程必须有个独立的线程计数器,这样就能保证线程之间不相互影响。
这儿注意下,假如线程执行是一个Java方式的时侯,计数器记录的是虚拟机字节码指令的地址;当执行的是Native的方式的时侯,计数器指令为空;该显存区域是Java虚拟机惟一没有规定任何OutOfMemoryError的区域。
2.Java虚拟栈,这个也是一个线程私有的,生命周期与线程是同步的,每位方式在执行的同时,还会创建一个栈帧,用于储存局部变量表,操作数栈,动态链接,技巧出入口等信息深入理解java虚拟机电子版,每位方式的调用到执行完成的过程就是一个栈帧入栈到出栈的过程;
这儿解释一下局部变量表,局部变量表储存方式相关的局部变量,包括基本数据,对象引用和返回地址等。在局部变量表中,只有long和double类型会占用2个局部变量空间(Slot,对于32位机器,一个Slot就是32个bit),其它都是1个Slot。须要注意的是,局部变量表是在编译时就早已确定好的,技巧运行所须要分配的空间在栈帧中是完全确定的,在技巧的生命周期内都不会改变。这部份东西我还想等下一篇博客的时侯我想仔细说一下字节码的执行过程;
虚拟机栈规定了2种异常情况,一种是线程恳求栈的深度小于虚拟机栈所容许的深度,这时侯将会抛出StackOverflowError异常,假如当Java虚拟机容许动态扩充虚拟机栈的时侯,当扩充的时侯没办法分配到显存的时侯才会报OutOfMemoryError异常;
3.本地方式栈,与虚拟机栈执行的基本相同,惟一的区别就是虚拟机栈是执行Java方式的,本地方式栈是执行native方式的;
4.Java堆,堆区是Java虚拟机所管理的显存中最大的一块,Java堆是被所有线程共享的显存区域,主要储存对象的实例。
当堆中没有显存完成实例分配,而且堆难以扩充的时侯,将会抛出OutOfMemoryError异常;当前虚拟机都是可以扩充的;
5.方式区,这个也是线程共享的显存区域,储存被虚拟机加载的类信息、常量、静态变量、即时编译的代码数据等;
方式区在化学上也是不须要连续的,可以选择固定大小或则扩充的大小,还可以选择不实现垃圾搜集,技巧区的垃圾回收是比较少的,这就是方式区为何被称为永久区的缘由,而且方式区也是可以执行回收的,该区域主要是针对常量池和类型的卸载;在方式区也规定当方式区未能满足内存分布的时侯,将会抛出OutOfMemoryError异常;
运行经常量是方式区的一部份,常量池主要用于储存编译生成的各类字面量和符合引用,因为常量池属于技巧区的一部份,所以当常量池没有显存空间的时侯就抛出OutOfMemoryError异常;
6.直接显存,不是虚拟机运行时的一部份,可以直接访问堆外的显存;所以当显存空间难以动态扩充的时侯才会出现OutOfMemoryError异常;
以上基本是JVM显存分布的内容,简单的理解水满则溢出就是这个道理,系统的整个空间是一个大的容器,分不同的部份或则桶去分担整个容量,当哪个桶不够的时侯自然会溢出。明白显存区域的分布我们看下对象是怎样分配在显存空间上面的?
Java对象这儿指的是引用类型的对象,这儿用Studentstu=newStudent()为反例访问,Studentstu作为引用对象,存在与Java虚拟机栈上,newStudent()保存在Java堆中,堆中记录Student类型的信息包括方式,插口深入理解java虚拟机电子版,对象类型等地址,这种类型的执行的数据储存在方式区中;
这儿须要说明一下对象访问的形式,主要包括2种句柄访问和直接表针访问:
1.句柄访问主要是Java堆中界定一块句柄池,虚拟机栈中储存句柄池中的地址,句柄池中包括对象的实例数据和对象类型的数据的地址,基本分布如右图:
2.直接表针访问,就是虚拟机栈直接指向Java堆中的对象类型表针和对象的实例数据,之后对象类型表针在指向方式区中对象类型的实例数据,分布如右图:
HotSpot就是第二种访问方法,优点在于访问速率快,省去一次表针开支时间,JVM显存分布基本介绍到这儿,接出来说下怎样保证正确回收?
回收是早已没有用的对象,那如何判定一个对象没用引用?这儿须要简单介绍2种方式:引用计数法和可达性剖析算法;
这儿简单说一下引用计数法:对象中添加一个引用计数器,每每有一个地方引用计数器就降低1,引用失效就降低1,计数器为0就不可用;缺点就在于难以处理对象直接互相引用的问题,由于互相引用之后未能使计数器为0,所以未能回收;
可达性剖析算法,也就是我们常说的GCRoot,,当一个对象没有与任何引用链相连的时侯,就可以对该对象进行回收,下边是Java中GCRoot对象使用的几个地方:
以上对象简单就是分为可用和不可用这2种,现今Java对引用概念进行扩展:
明白那些我们基本明白JVM怎样正确回收,接出来就是JVM哪些情况下触发GC以及GC触发的形式?
第一个问题比较容易回答其实是当显存空间不足的时侯就须要触发GC,GC回收的时侯采用的是分代搜集的算法,主要分为年青代和老年代,接出来我们简单介绍一下这2种方法:
年青代:当一个对象被创建的时侯,显存分配首先分配在年青代,大部份对象创建之后都不再使用,对象很快显得不可达,就是对象无用,因为垃圾是被年青代清除掉的,所以被称作MinorGC或则YoungGC。
老年代:对象假如在年青代存活了足够长的时间而没有被清除掉(即在几次YoungGC后存活了出来),则会被复制到年老代,年老代的空间通常比年青代大,能储存更多的对象,在年老代上发生的GC次数也比年青代少。当初老代显存不足时,将执行MajorGC,也叫FullGC。
明白这2块主要储存哪些东西之后接出来我们看下GC的整体结构,看一个对象怎样被Kill掉的流程:
1.当一个对象被创建的时侯(new)首先会在年青代的Eden区被创建,直至当GC的时侯,按照可达性算法,看一个对象是否衰落,没有衰落的对象会被装入年青带的Survivor区,衰落的直接被MinorGCKill掉;
2.步入到Survivor区的对象也不是安全的,当下一次MinorGC来的时侯还是会检测Enden和Survivor储存对象区域中对象是否存活,存活装入另外一块Survivor区域;
3.当2个Survivor区切换几次之后,会直接步入老年代,其实步入到老年代也不是安全的,当老年代显存空间不足的时侯,会触发MajorGC,早已衰落的仍然还是被Kill掉;
推荐一个这个写的很逗可以看下:
接出来我们还须要说一下GC的算法:标记--消除,复制,标记--整理这3种算法;
了解算法和GC显存分布之后我们接出来介绍垃圾回收器,这部份内容我不计划用文字去介绍,在第三个栏目我会将我对《深入理解Java虚拟机》这本书的思维导图,内容还不是很健全我正在整理中,并且有GC这部份内容包括各类参数配置,你们可以下载出来具体了解一下;
最后我们谈一下监控和优化,当初具备以上知识之后这种都将不是问题,所以工欲善其事必先利其器,这就是我要说的,剩下就是对工具操作,这种我觉得不须要介绍也是可以的,其实我也推荐一个博客:
三、结束语
思维导图工具:XMind
百度网盘地址:链接:密码:hox4这部份我还没有建立全,正在努力中,应当大约须要1周左右的时间;假如没有百度网盘可以加入我QQ群:438836709,可以一起沟通学习Java的经验,群上面我也分享好多PDF的书籍,总之欢迎你们加入吧!其实我也会把这个文件建立之后上传到QQ群上面!!