毕业以来技术上一直没有太大进步,仔细一想多是没有作技术分享,我喜欢把学习总结记录在印象笔记中,那么理解的是对是错也就没人能评判一下。为了技术进步,接下来将陆续把一些学习总结迁移到博客园中,欢迎你们多多指正!html
Jvmjava
Java虚拟机。一次编译,处处运行的前提算法
Jrespring
JVM+核心类库数组
Jdk多线程
JVM+核心类库+扩展类库jvm
JMMide
Java内存模型。主要用于多线程共享数据oop
子模块学习
自动内存管理(分配、回收内存)、虚拟机执行子系统(类加载机制、虚拟机字节码执行引擎)
JDK8之后:
(图摘自java3y)
程序计数器:
当前线程所执行的字节码的行号指示器。
Java虚拟机栈:
Java方法执行的内存模型:每一个方法执行时都会建立一个栈帧用于存储局部变量表、操做数栈、动态链接、方法出口等。
本地方法栈:
为虚拟机执行的native方法服务。
Java堆:
存放对象实例。
常量池:常量池记录的是代码出现过的常量、类名、成员变量等以及符号引用(类引用、方法引用,成员变量引用等)。
方法区(元空间):
存储已被虚拟机加载的类元数据信息。
内存溢出:
内存不够用。OutOfMemoryError: Java heap space、StackOverFlowError、OutOfMemoryError: Metaspace
内存泄漏:
无用内存未及时回收。最终可能致使内存溢出。
示例:
一、各类内存溢出。二、建立String对象时的内存分配
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * jvm内存溢出示例 * -Xms 堆初始值 * -Xmx 堆最大值 * -Xmn 新生代大小 * -Xss 每一个线程的栈大小 * -server -Xmx20m -Xms20m -Xmn10m -Xss1m -XX:+HeapDumpOnOutOfMemoryError * -server -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC */ public class MemErrorDemo { int depth = 0; /** * 内存溢出错误(OOM) */ public void OOMError() { List<byte[]> list = new ArrayList<>(); int i = 0; while (true) { list.add(new byte[5 * 1024 * 1024]); System.out.println("loop count: " + (++i)); } } /** * 栈溢出错误(StackOverFlowError) */ public void SOFError() { try { depth++; SOFError(); } finally { System.out.println("递归count: " + depth); } } /** * 元空间错误 * 使用cglib生成动态代理类 */ public void MetaSpaceError() { int i = 0; try { while (true) { i++; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhancer.create(); } } catch (Exception e) { System.out.println("generate class count" + i); e.printStackTrace(); } } static class OOMObject { } public static void main(String args[]) { MemErrorDemo memErrorDemo = new MemErrorDemo(); //memErrorDemo.OOMError(); //memErrorDemo.SOFError(); memErrorDemo.MetaSpaceError(); } }
/** * 字符串对象建立时内存分配 * 字符串常量池(内容在编译期肯定)、堆 * 编译期、运行期 */ public class StringObjDemo { public static void main(String args[]) { String a = "he"; String b = "llo"; String c = "hello"; String d = "he" + "llo"; String e = new String("hello"); String f = a + "llo"; String g = a + b; String h = e.intern(); System.out.println(c == d); System.out.println(c == e); System.out.println(c == f); System.out.println(c == g); System.out.println(f == g); System.out.println(c == h); } }
内容:废弃的对象、常量和无用的类;区域:堆、元空间
引用记数法:
难解决两个或多个对象的循环引用情况。
可达性分析算法:
可做为GCRoots的对象包括:
一、 虚拟机栈中(栈帧中本地变量表)中引用的对象。
二、 元空间中静态属性引用的对象
三、 常量池中常量引用的对象
四、 本地方法栈中native方法引用的对象。
强引用:平时使用最多的的引用,若对象有强引用,而且从GCRoots到其可达(可达性分析),则不会被GC回收。
软引用:在堆内存未发生溢出时不会回收有软引用的对象,在内存溢出将要发生前,先对软引用关联的对象进行回收,回收后仍溢出,则抛OOM异常。
弱引用:对象只有弱引用,会在下一次GC进行回收时被回收
虚引用:不能经过虚引用获取对象实例,不影响对象生存时间,惟一做用是在对象回收时收到一个系统通知
标记-清除算法:标记出须要回收的对象,而后清楚有标记的对象。
标记-整理算法:标记出须要回收的对象,而后移动到一端,直接清理边界外的内存。
复制算法:将内存容量分两部分,保持在某一时刻始终有一部分是空的,将仍然存活的对象复制到此区域。
分代收集算法:
新生代:复制算法。朝生夕死,对象存活率低,将区域分3份,eden:survivor0:survivor1=8:1:1
老年代:标记-清楚/标记-整理算法。
回收事件:
新生代回收事件:minor gc
老年代回收事件:major gc
所有回收(包括MetaSpace):full gc(Stop the world),默认堆空间使用到达80%(可调整)的时候会触发fgc
一、 对象优先在Eden分配
二、 大对象直接进入老年代
三、 长期存活的对象进入老年代。每熬过一次minor gc年龄增长1岁,默认15岁进入老年代。
四、 动态对象年龄判断。Survivor空间中相同年龄全部对象大小的和大于survivor空间的一半,大于或等于该年龄的对象直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。
五、 空间分配担保。新生代进行复制回收时,survivor空间不够用,survivor没法容纳的对象进入老年代。
加载、验证、准备、初始化和卸载顺序肯定,解析不必定,有时会在初始化以后——java运行时绑定(动态绑定/晚期绑定)。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用。
初始化的5种状况,若是类未初始化则马上初始化:
一、 遇到new、getstatic、putstatic、或invokestatic4条字节码指令。这4条指令常见场景:使用new关键字实例化对象、读取一个类的静态字段(被final修饰、已在编译期将结果放入常量池的静态字段除外)、调用一个类的静态方法。
二、 使用java.lang.reflect包的方法对类反射调用。
三、 初始化一个类的时候,若是父类未初始化,则优先初始化父类(接口除外,只在使用时初始化)。
四、 虚拟机启动时,用户须要指定一个要执行的主类(main方法所在类),虚拟机会先初始化这个主类。
五、 使用动态语言时,若是一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,其所对应的类未初始化,则先触发其初始化。
这5类是对一个类进行主动引用,除此以外全部引用类的方式都不会触发初始化,称为被动引用。
示例:类的被动引用
/** * 类加载阶段,被动引用示例 * 一、经过子类引用父类静态字段,不会致使子类初始化 * 二、经过数组定义引用类不会触发此类的初始化。 * 数组自己不经过类加载器建立,由java虚拟机直接建立。 * 三、常量在编译期存入调用类的常量池,本质上没有直接引用到定义常量的类 */ public class ClassLoadingDemo { public static void main(String[] args) { //System.out.println(SubClass.h); //SubClass[] scs = new SubClass[10]; System.out.println(SubClass.w); } } class SuperClass { static { System.out.println("SuperClass init"); } public static String h = "hello"; public static final String w = "world"; } class SubClass extends SuperClass{ static { System.out.println("SubClass init"); } }
即便两个类来自同一个class文件,被同一个虚拟机加载,只要加载他们的类加载器不一样,那这两个类一定不相等。
双亲委派模型
对类的加载请求,优先让父加载器加载,只有父加载器反馈没法加载时,子加载器才会尝试加载。
类加载器之间的父子关系并非经过继承来实现的,而是经过组合关系。
Class文件的常量池中存在大量的符号引用,这些符号引用一部分会在类加载阶段或者第一次使用时转化为直接引用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引用,称为动态链接。
目的:惟一任务是肯定方法调用的版本即调用哪个方法。
理解概念:静态类型、实际类型
解析调用:
静态过程,在编译阶段就彻底肯定,在类装载的解析阶段就会把涉及的符号引用所有转化为直接引用,不会延迟到运行期。
在解析阶段肯定惟一的版本,此类方法包括:静态方法、私有方法、实例构造器、父类方法4类。
分派:
理解概念:多态
方法的宗量:方法的接收者与方法的参数
静态分派经过静态类型肯定方法的执行版本,发生在编译阶段。典型应用:方法的重载
示例:方法的重载
/** * 静态分派示例 * 根据静态类型肯定方法版本 */ public class MethodOverLoadDemo { public static class SuperClass { } public static class Sub1 extends SuperClass { } public static class Sub2 extends SuperClass { } public void sayHello(SuperClass s) { System.out.println("hello super"); } public void sayHello(Sub1 s) { System.out.println("hello sub1"); } public void sayHello(Sub2 s) { System.out.println("hello sub2"); } public static void main(String[] args) { SuperClass s1 = new Sub1(); SuperClass s2 = new Sub2(); MethodOverLoadDemo ml= new MethodOverLoadDemo(); ml.sayHello(s1); ml.sayHello(s2); } }
动态分派
Java语言是一门静态多分派,动态单分派的语言。静态分派时经过方法接受者即静态类型、方法参数两个宗量肯定方法版本,动态分派时则在已有版本(静态分派时肯定)中只经过方法接收者即实际类型肯定最终方法版本。
典型应用:方法的重写
示例:方法的重写
/** * 动态分派示例 * 方法重写-jvm选择方法版本: * 一、在编译期间根据静态类型选择一个方法版本 * 二、在运行期间根据实际类型和编译期间已选的版本选择最终版本 */ public class MethodOverrideDemo { public static class O { public void m1(O o) { System.out.println("O-m1"); } } public static class A extends O { public void m1(A a) { System.out.println("A-m1"); } } public static class B extends A { @Override public void m1(A a) { System.out.println("B-m1"); } public void m1(B b) { System.out.println("B-m2"); } public void m1(O b) { System.out.println("B-m3"); } } public static void main(String[] args) { A a = new B(); a.m1(a); B b = new B(); a.m1(b); b.m1(b); } }
参考资料:
《深刻理解Java虚拟机——JVM高级特性与最佳实践》 周志明 著
Java3y: https://www.cnblogs.com/Java3y/p/9296496.html