jvm是Java代码运行的环境,他将java程序翻译成为机器可以可以识别的机器码,可以跨平台运行如linuc或者windos
简单说一下我对jvm运行的理解,
首先我们运行程序的时候,类加载器会将类按需加载到元空间/方法区 里面
然后启动线程的时候,jvm就会给每个线程分配一个独立的栈 ,如果有native方法就会有一个本地方法栈 ,对应的每个线程会有一个 程序计数器 用于记录线程运行状态的,线程运行到每一个方法就会有一个栈帧 ,方法执行完, 栈帧就会被释放,后进先出,当线程运行完成,栈也就释放了
在线程运行过程中,所新建和操作的一下临时对象就就会在堆中创建然后被引用,当线程执行完成,没有引用的对象,在下一次垃圾回收执行的时候被清理。大体流程就是这样
类的加载 触发:是按需要加载的,当被new到,静态方法或静态成员被访问到,反射操作等就会触发类的加载,
类的加载过程:加载,连接,初始化,使用,销毁
加载 阶段:工具类名将类的字节码通过文件、网络等方式读取到内存中,然后解析加载到方法区/元空间中。
加载阶段完成之后就到连接
连接分为3个步骤,首先是验证,验证字节码结构,语义是否复java约束规范 例如开头是否 cafe babe,验证通过就到准备阶段,给静态变量分配内存,给初始值,常量赋值等
最后是解析阶段,将符合引用替换为直接引用,解析为直接内存地址,链接阶段就算完成了
最后就是初始化 了 ,静态变量初始值,执行静态快
初始化完成就是可以使用了,还有就是就是销毁,虽然这个销毁很少发生 在内存不足full GC的时候 还是有可能被销毁的
类加载过程是按双亲委派模型 的
因为类的加载器有 启动类加载器,扩展类加载器,应用类加载器,每个加载器各自负责加载不通的类,启动类加载java运行时环境所需的类 如string所在的lang包,list,map所在的util包,IO包等,扩展类加载器就是加载bin/ext目录下的第三方库。应用类加载器就是加载我们写的类的,所谓的双亲委派就是 一个类的加载器收到请求,会先去尝试让父类加载,向上委派,如果父类加载过就不需要再加载,例如应用类加载器收到请求 会委派给扩展类加载器,扩展类加载器又委派给启动类加载器,这样做的好处就是确保安全,避免核心类被加载,避免重复加载保证唯一。
类加载好了,在程序启动线程的适合,jvm就会给线程创建一个栈
这个栈是线程独有的,线程执行完,栈就会自动释放
在线程的运行过程中每执行到一个方法就会创建一个栈帧, 这个栈帧记录着方法执行的操作和数据。
栈帧保存的数据有,局部变量表 ,操作数栈 (如果加减乘除的结果,操作完成就字符,先先进后出),还有动态链接 (方法内存地址,和引用),方法出口 (方法执行完成只有要干什么)
方法执行完栈帧也就出来了,他们后进先出的
线程运行过程程序计数器记录 着 线程的运行位置,在多线程情况下,上下文切换的适合让程序继续执行。
栈也会内存溢出 只是发生的比较少,还有一个深度不够的异常,就是线程执行的方法太深,如递归,不过这些发生的情况还是很少的。栈就大概这么了
线程的运行过程中 会产生很多的引用对象,这些读写被jvm分配到 堆 中,
堆是jvm最大的一个区域,也是我们最关注的,问题也是最频繁的,处理不当就会产生内存溢出或内存泄漏,
首先是内存分配,对象被创建出jvm有几种内存分配方式
1指针碰撞 ,如果堆的内存充足而且规则连续,就会先数组移动指针一样分配,这个成为指针碰撞,这是简单,快速的
2空闲列表 :就是在内存不规则的情况下,jvm会维护一个空闲内存的列表,然后把对象分散的分配
空间的是否规则要看选用的什么类型的垃圾回收机制,有压缩整理功能的就会有规则的空间
++serial,parNew(没有压缩整理),CMS,G1(sweep)(没有)++
3 本地线程分配缓 存 简称TLAB ,就是给每个线程分配一个独立的堆空间用于,使用完自己的才去使用公共的。
对象的组成包含3部分,对象头,实例数据,补齐填充
对象头主要包含:自身运行的数据,hash码,GC分代年龄,锁状态标志,偏凉锁线程id等
实例数据:就是代码中各种成员变量
补齐填充:占位用的,就是为了补齐数 达到某个整数被,提高CPU的访问效率
堆被分新生代和老年,他们的占比默认是占1/3和2/3,
其中新生代又分为eden区和Survivor区,Survivor又分为s1和s2区,他们的默认比例8:1:1,
在垃圾回收的时候 会先把eden区的回收,然后把存活的对象放到s区,经过多次垃圾回收还存活的对象就会升等到老年区,只有在full gc的时候 老年区才会被回收
进入老年区的情况有以下几种,1就是到达阈值指定的,2到达阈值的大对象,3动态年龄判断,就是s区超过50%之后年龄大但却没有到阈值的,4老年代空间担保机制(每次mionr gc的时候都会先判断老年代剩余空间是否大于新生代的,如果大于就直接gc ,如果小于就会在判断 是否大于每次平均晋升值,如果小于就直接full gc,如果大于就minor gc ,如果值多就会直接放到老年代)