->java体系结构
1、java体系结构:由java语言,class文件,java api和虚拟机组成。
2、java运行机制:a.源码编译为.class文件(java会被编译器编译为class字节码文件);b.jvm的类加载器装载calss文件;c.jvm找到main()入口,开始运行class(二进制字节码,JNI接口是java本地接口,装载class文件);d.jvm开始垃圾回收。
3、类加载器:分为4种:启动类加载器(负责加载JDK中的核心类库);扩展类加载器(负责加载Java的扩展类库);系统类加载器(负责加载应用程序classpath目录下所有jar和class文件);自定义加载器(加载自己定义的class)。
以上除了第一种加载器,其他加载器最终都依赖于第一种启动类加载器(也就是所说的双亲委派模式,将加载任务委派给他的双亲加载器)。
加载器具体工作步骤:
a、加载:寻找并导入Class文件的二进制信息,在堆中生成class对象
b、连接:进行验证、准备和解析(通过安全机制进行安全验证)
1)验证:确保导入类型的正确性
2)准备:为类型分配内存并初始化为默认值
3)解析:将字符引用解析为直接引用
c、初始化:调用Java代码,初始化类变量为指定初始值,如果子类初始化,会先初始化其父类(jvm调用
4、java安全性:主要由类装载器结构(定义信任类库边界,保护域,双亲委派模式)、
class文件检验器(4次检验:class文件结构,类型数据的语意检查,字节码验证,符号引用的验证)、
基于内置的jvm及语言的安全特性(安全类型转换,没有指针,自动垃圾回收,数组界限,null引用)、
安全管理器(定义了“沙盒”的外部边界)和JAVA API(api执行活动会先经过安全管理器,找到再执行)组成。
4.1、jvm内存模型:jvm中堆可称为主存,每一个线程都有自己的工作内存,下图解释了线程间如何通信的:
->java虚拟机
5、java虚拟机:内部有两种线程:守护线程(jvm自己使用,处理gc等);非守护线程(执行mian()的初始线程)。jvm的内部体系结构如下:
其中方法区(存运行时常量池、已被jvm加载的类信息、常量、静态变量、即时编译器编译后的代码等数据)
和堆(存创建的对象实例)是线程共享的(一个jvm实例有一个方法区和堆,需要gc回收,会有OutOfMemoryError异常)。
JAVA栈(栈帧:存的是局部变量、参数、返回值、对象引用等,会有StackOverflowError异常)、PC寄存器(存下一条执行的指令)
和本地方法栈(本地方法)是一个线程独享的(随线程结束而结束)。
6、数据类型:
7、class文件:一个calss文件都有一个常量池(保存所有的引用符号)
8、垃圾回收机制(gc):释放不被引用的对象的堆空间和堆碎块(方法区中回收废弃常量和无用的类)。判断对象是否存活的算法有:
a.引用计数算法(堆的对象实例都有一个引用计数,被创建时为1,任何其它变量被赋值为这个对象的引用+1,引用计数=0时可被回收。优点:速度快;缺点:无法检测出循环引用)。
b.可达性分析算法(把GC Roots的对象作为起始点,开始向下搜索,当一个对象到GC Roots没有任何引用,则不存活。被第一次标记,再次刷选,对象是否有finalize(),如果finalize()中没复活,则被第二次标记,然后才会被回收。可作为GC Roots的对象包括下面几种:
1) 虚拟机栈中引用的对象(栈帧中的本地变量表);
2) 方法区中类静态属性引用的对象;
3) 方法区中常量引用的对象;
4) 本地方法栈中JNI(Native方法)引用的对象。
)。
java中引用分为:强引用(类似Object obj = new Object(),强引用还在,就算存活)、软引用(如果系统要内存溢出,弱引用将会在第二次回收中被回收)、弱引用(下次回收前被回收,不管内存)、虚引用:
常用的垃圾收集算法:
a.标记-清除算法:从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收(缺点:造成内存碎片)。
b.复制算法:把堆分为对象区和空闲区,当对象区满了,扫描活动对象,把它们copy到空闲区,两个区就倒了过来(由于copy对象到空闲,所以不会造成内存碎片)。
c.标记-整理算法:和标记-清除一样,回收后多了一步操作,会将所有的存活对象往左端空闲空间移动,并更新对应的指针(解决了内存碎片)。
d.分代收集算法:根据对象存活周期把内存分为新生代(生命周期短的,如局部变量等,在堆中)、老年代(周期长的,如缓存对象、单例对象等,在堆中)和永久代(静态文件,如Java类、方法等,在方法区中,jdk1.8以后,改为元数据区,使用本地内存了)。
新生代算法:内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区。大部分对象在eden,回收时,eden存活对象copy至survivor0,清空eden;当survivor0也满了,再把eden、survivor0存活对象copy至survivor1,清空eden、survivor0,再把survivor0和survivor1交换。
以上是新生代回收,也叫Minor GC。(频率较高)
老年代算法:当survivor1也满了,把存活对象copy至老年代,触发一次Full GC(新生代、老年代都回收)。其中内存比新生代也大很多(大概比例是1:2)。
永久代算法:回收废弃常量(可达性分析算法标记)、无用的类(该类所有的实例都已经被回收)。
收集器:
Serial收集器(复制算法)
新生代单线程收集器,标记和清理都是单线程,优点是简单高效。是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定。
Serial Old收集器(标记-整理算法)
老年代单线程收集器,Serial收集器的老年代版本。
ParNew收集器(停止-复制算法)
新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。
Parallel Scavenge收集器(停止-复制算法)
并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般为99%, 吞吐量= 用户线程时间/(用户线程时间+GC线程时间)。适合后台应用等对交互相应要求不高的场景。是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数。
Parallel Old收集器(停止-复制算法)
Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先。
CMS(Concurrent Mark Sweep)收集器(标记-清理算法)
高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu 追求高响应时间的选择。
G1收集器(jdk1.7以后支持,试图取代CMS)
G1适用于新生代和老年代,而CMS只适用于老年代。
9、监听器:支持互斥和协作两种线程。监听器模型如下:
可以使用如下方法让线程等待/唤醒:
10、jvm配置参数:分为三类,
a.跟踪参数(跟踪监控jvm状态):-XX:+PrintGC或-verbose:gc参数(打印GC简要信息);-XX:+PrintGCDetails参数(打印GC的详细信息以及堆使用详细信息);-Xloggc:log/gc.log(外部文件记录GC的日志);-XX:+TraceClassLoading(监控类的加载)。
b.堆分配参数(分配堆内存):-Xmx(最大堆);-Xms(最小堆);Xmn(新生代大小);-XX:PermSize(永久区初始值) -XX:MaxPermSize(永久区最大值)。
c.栈分配参数(分配栈内存):-Xss(栈大小)。
(官方推荐新生代占堆的3/8,幸存代占新生代的1/10)
11、jvm性能监控工具:jps(JVM 进程状况工具);jstat(JVM 统计信息监控工具);jinfo(Java 配置信息);jmap(Java 内存映射工具);jhat(JVM 堆快照分析工具);jstack(Java 堆栈跟踪工具);(参考:https://www.sohu.com/a/199863043_827544)