Java是如何实现跨平台性的
java是靠JVM实现的跨平台性的
jvm通过classLoader加载字节码文件,加载到运行时内存,运行时内存包含,方法区,堆区,栈区,线程,程序计数器等组成,加载到运行时内存的数据会通过程序执行引擎,和操作系统交互,其中执行引擎中有个最u重要的就是垃圾回收器,还有就是本地方法,它主要供Java调用底层的c实现
类从编译到执行的过程
编译器将某个 .java 源文件,编译成 .class 字节码文件
classloader将字节码文件,转换为jvm中的Class对象,Class对象的泛型就是编译后的对象,jvm通过Class<对象> 实例化初这个对象,供程序使用
类加载机制和双亲委派模型
类加载器是将class文件加载到jvm中
类加载需要遵守双亲委派机制
类加载器有:自定义加载器,系统/应用加载器,扩展加载器,启动类/根类加载器
**自定义加载器:**不是必须存在的
**系统应用加载器:**主要加载classpath指定的内容
**扩展类加载器:**主要加载ext目录下的jar包
**启动类/根类加载器:**主要加载lib目录下的jar包
当程序需要一个类的时候他的加载顺序是:
先调用自定义加载器,查找是否加载过,加载给直接返回,如果没有加载就委托给系统应用加载器,看是否加载过,如果没有加载过就向扩展应用加载器委托,如果没有就再向上委托给启动类根类加载器,启动类更根类加载器会到lib目录下查找,如果启动类加载器也没有找到,就会告诉扩展类加载器没有找到,然后扩展类加载器会到ext目录下查找,如果没有找到就会通知应用程序加载器,应用程序加载器会再应用级别,或者classpath目录下查找如果没有就会交给自定义加载器,如果自定义加载器也没有找到就会报Class Not Found异常
jvm分为哪些区域
分为方法区,堆,程序计数器,本地方法栈,和虚拟机栈
程序计数器:每个线程私有的,存放的是当前线程锁执行的字节码编号
虚拟机栈:线程私有的,每调用一个方法就会创建一个一个栈帧,主要存放局部变量表,操作数栈,动态链表,方法出口等信息
本地方法栈: 线程私有的,本地方法栈中主要存放的是其他语言写的,如C,C++
方法区: 是一个逻辑性的概念,1.7和1.8不同,1.7是称为元空间,1.8是称为元空间,永久代必须设置大小,元空间不用设置,1.8字符常量池也放在了元空间中
堆: 是线程公有的,每new一个对象就会在堆空间中开辟一块区域,栈帧中引用指针指向堆中的某一个对象,垃圾回收主要就是这一块区域
垃圾回收算法有哪些,优缺点是什么
垃圾回收算法:引用计数器,可达性分析(根可达算法)标记清除算法,复制算法,标记整理算法,分代回收算法;
引用计数法:
优点是实现简单,操作快,缺点是会出现循环引用问题,这样引用就永不为0,永远不会被垃圾回收
java中没有采用引用计数法,python中使用的是一引用计数法(但是已经解决循环引用问题)
实现原理:
当堆中有一个引用指向它就会加1 ,当计数器等于0时,会判定为垃圾
可达性分析(根可达算法):
所有对象都有一个可达的根,通过根,向下搜索,能搜索到的就不是垃圾,搜索不到的就是垃圾,不过判定为垃圾的对象就一定是垃圾,需要进行至少两次标记,如果经历至少两次标记之后仍然被判定为不可达对象,才会进行垃圾回收
优点:
实现简单
缺点:
会产生垃圾碎片
哪些对象可以做为GCRoots
可以作为GCRoots的对象有:方法区中的常量,方法区中的静态属性,活跃的线程引用对象,本地方法栈中的JIN的对象都可可以作为GCRoots的根
引用计数法,可达性分析(跟可达算法)都是用来判断哪些对象是垃圾的算法
回收算法
标记-清除算法
分为两个阶段,在标记阶段会从根开始向下搜索同时标记可达对象,没有标记就是垃圾,第二阶段会将堆中的对象从头到尾线性遍历如果发现兑现没有标记为可达对象就会进行回收
优点 实现简单
缺点 会产生内存碎片
为了解决标记清除算法的内存碎片问题就产生了复制算法
复制算法
复制算法是将内存分为大小相等的两块,每次使用其中一块,当一块用完之后,将所有存活的对象复制到另一半,然后把已经使用过的内存空间清除,每次都是堆其中一块内存空间进行回收,新生代用的就是复制算法
优点
解决了标记清除算法的内存碎片问题,只移动堆指针完成内存分配就可以,实现简单高效
** 缺点**
内存利用率低
标记整理算法
标记整理算法和标记清除算法在标记阶段实现方式相同,但是后续步骤不是直接堆可回收对象进行回收,而是对所有存活的对象都向一端移动,然后直接清除掉边界以外的数据
优点
没有内存碎片,内存利用率高
缺点
算法实现相对复杂
回收
jvm中堆空间是垃圾回收的组要区域,分为新生代和老年代,新生代占用堆空间的三分之一,老年代占用堆空间的三分之二,新生代又分为Eden区,from区,to区,Eden占新生代的十分之八,from区to区各占新生代的十分之一
每一次new时都会在堆中的Eden区创建一个对象,当发现空间满了机会将Eden区和from区中存活的对象移动到to区并且年龄加1,这时Eden区和from区就有空间了并将原来的to区变成from区,将原来的from区变成to区,然后再将对象放入Eden区,当满了再重复前面的步骤,在所有对象移动的过程中如果发现有对象已经成为垃圾了,在将to区转换成from区,将from区转换成to区后会回收掉垃圾,一直这样往复循环,直到Eden区和from区,to区满了会将所有的存活的对象移动到老年代,或者新生代的年龄已经大于15了(cms垃圾回收默认是6岁)也会移动到老年代
哪些情况下会将对象放入老年代
- 新生代已经没有空间了
- 新生代的年龄已经大于15
- 新申请的内存空间大于新生代的总和
- 动态年龄判断(并不是所有的对象直接放入新生代,当相同年龄的对象,所有大小超过心神带空间的一半也会直接放入老年代
新生代老年代用什么垃圾回收器
新生代是由migorGC老年代使用maxGC ,当触发MaxGC时会触发整个堆空间的fullGC
触发FullGC的条件
老年代空间不足
mingorGC晋升到老年代时,但是老年代内存空间不足
显示调用了System.GC()方法
触发FullGC有什么影响
触发了FullGC会出现 STW(stop the world)现象,会暂停所有的用户进程,启动垃圾回收线程,为了持续对外服务要尽可能防止FullGC
垃圾收集器
对于新生代老年代会使用不同的回收器
Serual,ParNew,Scauenge是新生代的垃圾回收器
Serial垃圾是串行的垃圾回收器
Paraler Secavenge是并行回收,并且关注吞吐量的,主要配合CMS使用
CMS SerialOld,Paraller Old 是老年代回收器
常见的组合有
SerIal+CMS
Serial+Serial Old
ParNew+CMS
ParNew+SerialOld 当CMS出现意外时 会退化为SerialOld
Paraller Scavenge+SerialOld
Paraller Scavenge和ParNew+ Paraller old的组合
1.8默认使用的是Paraller Scavenge + Parallel old的组合他们都是并行的
1.9采用的是G1垃圾回收器
11采用的是ZGC垃圾回收器
Serial和serial old有什么特点
新生代gc线程启动时,采用复制算法,并暂停所有的用户线程
当触发老年代的垃圾收时,老年代gc线程采用标记整理算法,并暂停所有的用户线程
Ps/ParNew + Serial Old有什么特点
新生代采用parllel Scavenge 或ParNew 老年代使用 Serial Old搭配
新生代采用并行的方式,使用复制算法,并暂停所有的用户线程
老年代使用串行的方法,使用标记整理算法,并暂停所有用户线程
CMS垃圾回收器
**初始标记 ** Stop the World 标记Gcrootd直接关联对象
并发标记 并发追溯标记,用户进程不会停顿
重新标记 暂停虚拟机,扫描CMS堆中的剩余对象
并发清理 清理垃圾对象,程序不会停顿
并发重置 重置CMS收集器的数据结构
用户线程到达某个安全点,如方法结束,循环结束,调用结束,此时就会进行初始标记会触发STW(stop the worlds) 标记的是GcRoots直接关联的对象,接下来进行并发标记阶段,用户线程和标记线程一起来做,在并发标记阶段用户线程不会站厅所以不会触发STW,进入下一个安全点时会进行重新标记,此时会暂停所有用户线程,,它会扫描cms堆中剩余的所有对象,剩余对象是只cms前两次标记后没有标记的对象,标记完成之后,会进行并发清理,此时gc线程和用户线程同步进行,最后重置收集器的数据结构
G1垃圾收集器
G1垃圾收集器是将整个JVM内存分为大小相等的Regin区,新生代和老年代不再物理隔离,是存在逻辑上的分代概念
G1是java9的默认垃圾回收器
G1在整体上使用标记整理算法,局部使用复制算法
G1的每个Region大小在1-32M之间,总的个数可以存在2048个,则堆的中大小可以达到64G
新创建的对象也是放在新生代中的,如果新new出来的对象,大于一个Regin的一半且小于一个region这是就把他放在一个region上并且直接把这个region标记为old,如果创建的对象大于一个region需要多少region就会分配多少region,此时这些region都会直接标记为old
可以通过-XX: G1HeapRegionSize=n指定Regina的大小
当堆空间不够的时也会进行GC操作操作,年轻代用YunangGC老年代用oldGC
当新对象添加到堆中时发现堆空间不够,会将Eden区和from区存活的对象移动到to区,但是to区不是实现分配好的,to区可能是空闲的,拷贝时使用复制算法,G1并没有针对老年代的垃圾回收期,因为并没有真正意义上的老年代,所以在做老年代的垃圾回收时会顺带将新生代也一起回收,这时叫做MixGc MixGc是G1垃圾收集器独有的。
mixGc有那两个特有的属性
MixGc有两个特有的概念 一个是Rset和Cset
每一个region都有一小块内存空间它叫做rset,rset的作用就是,Rset其他的region引用了这个region就会把他的地址放在rset
Cset本次进行垃圾回收了所有许哟啊被回收的region就叫做Cset
G1垃圾回收器的回收步骤
**初始标记:**只标记GCRoot能直接关联的对象
Root Region Scanning: 扫描整个old的region
**并发标记:**进行gcroot的追踪
**最终标记:**修整并发标记期间,因程序运行导致标记发生改变的那一部分对象
**清理回收:**根据时间进行价值最大化回收,重置Rset
G1常见的参数设置
-XX:UseG1Gc 设置使用G1垃圾回收器
-XX:MaxgcPuasemillis=n gc最大停顿时间
-XX:InitiatingHeapOccupancyPercent当老年代占用多少时会触发GC,默认值45%
-XX:G1ReservePercent 设置空闲的预留空间,百分比,默认百分之10
CMS和G1有什么区别
G1回收器有两个重要的特点:不会有内存碎片,能够精确控制GC最大停顿时间