目录
类加载过程
分为加载,连接和初始化的过程。
加载:
将.class文件的二进制字节流读入内存(jdk1.7之前是JVM内存,jdk1.8及之后为本地内存),并在内存中创建class对象
方法区分配内存问题:太小,OOM问题;太大,影响性能,内存泄露问题。
连接
验证,准备,解析
验证:保证字节流符合JVM规范,例如,检查类是否继承了final修饰的类,例如检查符号权限是否符合语法规定
准备:为类变量开辟空间并赋予默认值
基本类型(int)等的默认值为0;
引用类型默认为null;
解析:将class常量池中的符号引用转为直接引用
符号引用好比如test()方法,直接引用是偏移量例如地址0x123456
初始化
类的变量赋值
GC回收机制(垃圾回收)
对JVM内存进行标记,确定哪块内存会被回收。
区域
堆内存,方法区(jdk1.7以前),1.8以后改为元数据区
其它和线程共存亡
判断对象是否存活
引用计数法(为0可回收)
可达性分析法:GCroots对象到对象是否有引用链,finalize()方法可以拯救对象一次
GCroots:虚拟机栈,方法区类静态引用的对象,本地方法栈引用的对象
回收机制
标记清除法:标记就清除,会产生很多内存碎片,再次触发GC
标记整理法:标记清除后,将内存碎片连续。
复制算法:AB两块内存,先用再用
分代收集算法:年轻---复制算法,老年代---标记清除或者整理法
感觉像年轻和老人之间的缓冲区
HashMap
Map接口实现,就是字典嘛
遍历:
null对比
线程不安全:
jdk1.8以前
jdk1.8
hash冲突
内存泄露
无用的对象一直占内存,没有重写equels和hashcode方法,HashMap发现每次放进来的key的hash都不一样,然后就会不停地往集合里放新数据
类加载器
引导类加载器bootstrap:c语言实现,加载到的路径可以由System.getProperty(“sun.boot.class.path”)查看。
扩展类加载器extend:具体加载路径可通过System.getProperty(“java.ext.dir”)。
系统类加载器system:System.getProperty(“java.class.path”)查看加载路径
加载标识
判断一个类是否被加载的标识是:类全限定名+类加载器,例如pg包下的Person类被CL1类加载器加载到内存,唯一标识为(Person, pg, CL1)
加载机制
全盘负责:该类引用的类全都使用一个加载器加载,避免重复加载
双亲委派(父类委托):递归到顶级父类加载器加载, 将.class文件载入内存的过程由双亲委派机制完成的。
优点:避免多次加载类
安全,防止篡改API,核心API由引导类加载器加载
缓存
当jvm加载完成一个类是会将类放入jvm缓存中,加载流程为先去缓存区查看当前类是否被加载,如果没有则读.class文件并加载,如果加载则直接返回
自定义加载器:
如果打破双亲委派模型,那么你需要重写loadClass方法
JVM内存结构
在运行时数据区
程序计数器PCR:指向下一条将要执行的指令的地址。即线程执行流程是由 字节码解释器 来改变程序计数器的值来维持的。不存在内存溢出的。
堆:
栈:虚拟机栈是为我们的Java方法服务的,而本地方法栈则是为native方法服务的,二者功能基本相似,而我们平时所说的栈就是虚拟机栈。
嵌套调用类的执行过程可以看作栈。
方法区:为类被编译的数据加载到内存后的存储位置
在JDK1.7时,将字符串常量池与静态变量转移到了堆内存,在JDK1.8时取消了方法区,用元空间取代了方法区,元空间位于物理内存