本文主要是介绍【每日鲜蘑】学习JAVA必须知道的知识,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
简单梳理Java知识时,整理的笔记,分享一下,如有错误,还请指正,谢谢🙏
编译过程
编译由 Java 源码编译器(javac)来完成。
过程:源代码---【词法分析器】---Token 流---【语法分析器】---语法树---【语义分析器】---注解抽象语法树---【字节码生成器】---JVM 字节码
三个过程
语法糖【泛型】
泛型只会在 Java 源码中存在,编译过后会被替换为原来的原生类型(Raw Type,也称为裸类型)了。这个过程也被称之为“泛型擦除”。
- 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现 ClassCastException 异常】
跨平台运行
字节码是不区分系统的,而 JDK 是区分系统的。各个平台编译的字节码都符合 JVM 的规范,从而做到一次编译到处运行。
类的加载
不是所有的类都会被加载到 JVM 的,Java 类的加载是动态的,只有在需要用到的时候才会被加载,这样节省了内存开销。
默认的类加载器
- 启动类加载器:【Bootstrap ClassLoader】负责加载$JAVA_HOME 中 jre/lib/rt.jar里所有的 class,由 C++实现,不是 ClassLoader 子类。
- 扩展类加载器【Extension ClassLoader】负责加载 java 平台中扩展功能的一些 jar 包,包括$JAVA_HOME 中 jre/lib/*.jar 或-Djava.ext.dirs 指定目录下的 jar 包。
- 应用类加载器【Application ClassLoader】负责记载classpath中指定的 jar 包及目录中 class。
- 用户自定义类加载器【User ClassLoadder】
双亲委派【解决安全性问题】
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上。
- 好处: 防止内存中出现多份同样的字节码(安全性角度)
类加载器在成功加载某个类之后,会把得到的 java.lang.Class 类的实例缓存起来。下次再请求加载该类的时候,类加载器会直接使用缓存的类的实例,而不会尝试再次加载。
通过自定义 ClassLoader,并重写父类的 loadClass 方法可以打破双亲委派的机制,而 Java 的 SPI(服务提供发现机制)也是打破双亲委派机制的。
详细过程
- 加载,查找并加载类的二进制数据,在 Java 堆中也创建一个 java.lang.Class 类的对象。
- 连接,连接又包含三块内容:验证、准备、初始化。
- 1)验证,文件格式、元数据、字节码、符号引用验证;
- 2)准备,为类的静态变量分配内存,并将其初始化为默认值;
JIT 即时编译器
【Just In Time】将热点代码的字节码重新编译优化,让 CPU 直接执行,这样的执行效率会更高。HotSpot 是基于计数器来检测热点代码的而非采样。
类加载完后
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。
JVM 的垃圾回收算法
通过“可达性分析算法”分析哪些对象是垃圾,然后通过垃圾回收算法进行回收。
- Serial 收集器,串行收集器是最古老,最稳定以及效率高的收集器,但可能会产生较长的停顿,只使用一个线程去回收。
- ParNew 收集器,ParNew 收集器其实就是 Serial 收集器的多线程版本。
- Parallel 收集器,Parallel Scavenge 收集器类似 ParNew 收集器,Parallel 收集器更关注系统的吞吐量。
- Parallel Old 收集器,Parallel Old 是 Parallel Scavenge 收集器的老年代版本,使用多线程“标记-整理”算法
- CMS 收集器,CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它需要消耗额外的 CPU 和内存资源,在 CPU 和内存资源紧张,CPU 较少时,会加重系统负担。CMS无法处理浮动垃圾。CMS 的“标记-清除”算法,会导致大量空间碎片的产生。
- G1 收集器,G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。
JVM 内存模型
JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。
栈管运行,堆管存储。
JVM 内存会划分为堆内存和非堆内存,堆内存中也会划分为年轻代和老年代,而非堆内存则为永久代(方法区、元空间)。
年轻代又会分为 Eden 和 Survivor 区。Survivor 也会分为 FromPlace 和 ToPlace,toPlace 的 survivor 区域是空的。Eden,FromPlace 和 ToPlace 的默认占比为 8:1:1。
当然这个东西其实也可以通过一个 -XX:+UsePSAdaptiveSurvivorSizePolicy 参数来根据生成对象的速率动态调整
内存泄漏&内存溢出
- 内存溢出:装载类的空间不够、内存空间不足、内存泄漏等等都可能导致溢出。
线程栈
JVM 规范让每个 Java 线程拥有自己的独立的 JVM 栈,也就是 Java 方法的调用栈。
当方法调用的时候,会生成一个栈帧。栈帧是保存在虚拟机栈中的,栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息
线程运行过程中,只有一个栈帧是处于活跃状态,称为“当前活跃栈帧”,当前活动栈帧始终是虚拟机栈的栈顶元素。
JVM 年轻代到年老代的晋升过程的判断条件是什么
- 部分对象会在 From 和 To 区域中复制来复制去,如此交换 15 次(由 JVM 参数 MaxTenuringThreshold 决定,这个参数默认是 15),最终如果还是存活,就存入到老年代;
- 如果对象的大小大于 Eden 的二分之一会直接分配在 old,如果 old 也分配不下,会做一次 majorGC,如果小于 eden 的一半但是没有足够的空间,就进行 minorgc 也就是新生代 GC;
- minor gc 后,survivor 仍然放不下,则放到老年代;
- 动态年龄判断 ,大于等于某个年龄的对象超过了 survivor 空间一半 ,大于等于某个年龄的对象直接进入老年代;
JVM 出现 fullGC 很频繁,怎么去线上排查问题
- 是不是频繁创建了大对象(也有可能 eden 区设置过小)(大对象直接分配在老年代中,导致老年代空间不足--->从而频繁 gc)
- 是不是老年代的空间设置过小了(Minor GC 几个对象就大于老年代的剩余空间了) 1. 如果一次 full GC 后,剩余对象不多,那么说明 Eden 的空间设置太小,导致大量短生命周期的对象被分配到了老生代。 2. 如果一次 full GC 后,老生代的变化不大,那么是老生代分配空间太小了。
JVM 垃圾回收机制,何时触发 MinorGC 等操作
- 当 young gen 中的 eden 区分配满的时候触发 MinorGC(新生代的空间不够放的时候).
这篇关于【每日鲜蘑】学习JAVA必须知道的知识的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!