jvm笔记
jvm:
第二章:自动内存管理
运行时数据区
程序计数器,虚拟机栈,本地方法栈,堆,方法区()
对象创建过程
类加载:
分配内存:指针碰撞,空闲列表(多线程并发环境:1.Cas+重试机制 2.TLAB:Thread local allocate buffer)
实例化:
内存布局
对象头:
1.存储对象运行时的数据:hashcode,gc年龄,锁状态,线程持有的锁,偏向线程id,偏向时间戳
2.类型指针:即对象执行它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
实例数据,
对其填充
对象的访问定位:
句柄池:reference存储的是句柄池。句柄池(对象数据的地址 + 类型数据的地址) ,但对象内存地址变化时,不需要修改引用reference的地址,只需需要句柄池即可
直接指针:reference存储的是对象的内存地址。默认使用这种方式,减少映射的时间
对象已死?
引用计数法:相互引用时无法删除
可达性分析:GC Roots作为根节点,向下开始搜索,走过的路径称应用链,对象到引用链无任何引用,不可达,即可以回收
第三章:垃圾收集器与内存分配
垃圾收集器
serial
parNew
parallelScavage
cms
serial Old
parallelOld
g1
zgc
-XX:UseSerialGC serial+serialOld
-XX:UseParNewGC parNew+serialOld
-XX:UseParavalGC paraval+serialOld
-XX:UseParavalOldGC paraval+paravalOld
-XX:UseConcuMarkSwapGC ParNew+cms+serialOld
-XX:+UseG1GC 目标:替换掉cms收集器, 并行与并发:减少stw的时间、分代收集、空间整合(不会产生碎片,全局看是标记-整理,局部看是复制 )、可预测的停顿
内存分配和回收策略
新对象在eden区分配
大对象在老年代分配
存活久的进入老年代
动态年龄分配
空间担保机制
第四章 虚拟机性能监控与故障处理工具
jdk的命令工具
jps,jstat,jinfo,jmap,jhat,jstack,jconsole,virtualVM
第六章:类文件结构:
class类文件结构:魔数+版本,常量池,访问标识,类索引,父类索引,接口索引集合,字段,方法,属性
字节码指令:iload,istore,iadd,...
第七章 虚拟机类加载机制
类的加载过程:
加载:1.通过类的全限定名获取二进制字节流 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 3.在内存中生成一个代表这个类的java.lang.Class对象,做个方法区这个类的各种数据访问入口 (Proxy用了ProxyGenerator.generateProxyClass 为接口生成 *$Proxy 的代理类的二进制字节流)
验证:是否符合虚拟机要求,是否危害虚拟机安全 (文件格式验证,元数据验证(如继承父类方法等是否合规),字节码验证(对方法体校验),符号引用验证)
准备: 为类变量分配内存并设置类变量初始值 (静态变量或常量设置初始值,不包含实例变量(实例变量在对象实例化时随着对象一起分配在java堆中))
解析:虚拟机将常量池内的符号引用替换为直接引用的过程。 直接引用(直接指向对象的指针)
初始化:开始执行类中定义的java代码
类加载器:bootstrap classloader,extenstion classloader,application classloader
双亲委派模型:当一个类加载器接受类加载请求时,会委托给父类加载器进行加载,以此类推,如果父类加载器加载不了,才有子加载器进行加载
好处:保证java稳定运行,不会出现一个类 有多个类加载器进行加载 而得到不同的类 (判断是否同个类:类本身+类加载器)
破坏双亲委派模型:
第一次破坏:自定义类加载器 继承ClassLoader,覆盖findClass()方法 ,而loadClass()方法里面实现了双亲委派模型
第二次破坏:基础类要调用用户的代码怎么办?
如JNDI(java命名目录接口服务):jndi目的是对资源进行集中管理和查找,jndi的代码由启动类加载器去加载。他需要调用厂商实现的jdni提供者的代码,但启动类加载器不可能认识这些代码怎么办? 线程上下文类加载器(Thread Context ClassLoader),这个类加载器通过Thread的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将从父线程中继承一个,如果在应用程序的全局都没设置,那这个类加载器默认就是应用程序类加载器 。 jndi服务使用这个线程上下文类加载器加载需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作,也就是违背了双亲委派模型 。java所有涉及SPI加载动作基本上都使用这种方式,如JNDI,JDBC,JCE,JAXB,JBI等
第三次破坏:由于用户对程序动态性的追求而导致的,动态性指的是代码热替换,如接上鼠标,U盘,不用重启停机就能立即使用
扩展:OSGI(Open Service Gateway Initiative 动态化模块系统)实现模块化热部署的关键则是它自定义的类加载器机制的实现,每个程序模块(OSGI中称为Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉来实现代码的热替换。所以OSGI的类记载其不再是双亲委派模型
第八章 虚拟机字节码执行引擎
运行时栈帧结构:1个方法1个栈帧 ,每个栈帧包含:局部变量表,操作数栈,动态链接,返回地址
方法调用:
解析:静态方法,私有方法,实例构造器,父类方法(编译器可知,运行期不可变)
分派
静态分派:方法重载
动态分派:方法重写
其他:
1.调优参数
栈:-Xss
堆:-Xms -Xmx -Xmn -XX:NewSize -XX:MaxNewSize -XX:NewRatio -XX:SuviorRatio
方法区:-XX:PermSize -XX:MaxPermSize -XX:MetaspaceSize -XX:MaxMetaspaceSize
2.GC作用、GC原理,GC机制
GC:在适当的时候回收jvm中的垃圾。什么时候?谁?如何回收?
1.什么时候:
执行system.gc()
新生代空间不足
老年代空间不足
2.谁:
对象不可达
3.如何回收:
有三种回收算法:复制,标记-清除,标记-整理
垃圾回收器:serial,parNew,parallelScavage,cms,serialOld parallelOld G1,zgc
https://blog.csdn.net/liewen_/article/details/83151227
为什么重写equals 一定要重写hashcode()?
两个对象相等,他们的hashcode一定相等。 如果两个对象equals相等,而不重写hashcode,我们知道hashcode是object的方法,那么比较的是这两个对象的内存地址,很明显,是不相等的,这就和上面说的(两个对象相等,他们的hashcode一定相等)矛盾了。
为什么要有hashcode?
比如往hashset中添加元素,如果原先有了n个元素,添加是,如果没用hashcode的话,那么就要遍历所有的元素进行equals比较。性能低下,如果使用hashcode比较的话,如果不等,说明在hashset中没有该元素,可直接添加,如果相等(hash碰撞),再判断equals是否相等