我们coding完后点击IDE的运行,程序就跑起来了,怎么回事?
首先我们写的源文件叫.java文件,然后点击IDE的运行在硬盘会生成.class字节码文件,接着Java虚拟机从硬盘加载.class字节码文件,再者内部操作和解析成电脑能识别的机器码,最后CPU执行
我们要重点关注的下面框框的部分,也就是JVM了
别那么着急,首先得看看JVM的体系结构:
这时上面框框的内容就可以稍微详细一点了
注意这时的图并不是完整且准确的,为了简便而改名或省略了,最后面会放出完整图的
那么,我们从上往下开始认识这些结构
负责加载.class字节码文件到 Java 虚拟机中,只有把文件放入虚拟机才能被读取
当然是动态加载的!即需要用到时才加载,这样节省了很多内存空间,防止内存溢出
那就是类的加载器了,类加载器默认有三种,还有一个自定义类加载器:
这里就有一个看起来高大尚的名词双亲委派,不用觉得很难,所谓的双亲委派指的是:
为了安全性
缓存机制:还有某个类被加载后就会把类的实例放到内存中,下次直接用内存中实例,不用再次加载了
加载过程分类三步:
类加载完后就开始给新生对象分配内存了,先来look look 虚拟机的内存结构把
浅绿色为线程共享
浅橙色为线程私有
其中:
简述一下内存分配
准备了两个类
public class BeanTest {
private int id;
private String name;
//各种Getters/Setters
}
public class JVMTest {
public static void main(String[] args) {
BeanTest beanTest = new BeanTest();
beanTest.setName("Howl");
System.out.println(beanTest.getName());
}
}
BeanTest beanTest = new BeanTest();
,方法区没有这个类的元数据,动态加载beanTest.setName("Howl");
,该实例根据指向去方法区找到对应类的元数据(方法表),获取对应函数的字节码地址当然是根据调配的指令顺序,依次执行程序指令拉
这里面有个技术需要讲一下下,JIT即时编辑器
JVM加载了.class文件后逐条读取并解析成机器码给CPU执行,我们当然不满足于此,有没有方法提高效率呢?
答案是有的,用 JIT即时编辑器
思路是这样的:
我们Sun公司使用的虚拟机是HotSpot,它采用计数器的方式(方法调用计数器,回边计数器)来判断是否为热点代码,当超过一定的数值时判断为热点代码
是时候放出稍微标准的图了,顺带提一下没有讲到的内容
新生代:存放刚创建和年轻的对象,若生存足够长(15)没有被回收会被移入老年代,这里会频繁创建也就会频繁MinorGC垃圾回收
Minor GC(复制算法):
Eden:新对象的出生地 (新对象若过大,直接分配到老年区),Eden内存不够会触发Minor GC
ServivorFrom:保留了一次MinorGC过程中的幸存者
ServivorTo:上一次GC的幸存者,作为这一次GC的被扫描者
老年代:对象比较稳定,所以Major GC不会频繁执行。进行Major GG前一般会先进行一次Minor GC,使得有新生代的对象晋入老年代,导致空间不够用才触发,当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次Major GC进行垃圾回收腾出空间
Major GC(标记-清除算法):
永久代:内存的永久保存区域,主要存放class和Meta(元数据),GC不会在主程序运行期间对永久区域进行清理,这导致永久代区域随着类加载的class增多而胀满,最终抛出OOM(Out Of Memory)异常
在1.8中,永久代被直接空间里的元空间代替,即大小受实际内存限制,不是虚拟机限制
Full GC、Major GC
Full GC:收集年轻代,老年代,永久代 Major GC:只收集老年代
程序在运行过程中,虚拟机会自动帮我们清理程序中不再需要的垃圾,减轻内存负担。
堆是垃圾回收的主战场,里面存放了大量实例
不像C语言,申请空间后需要free()来释放空间,但也不要因为有垃圾回收就不理会内存了
怎么判断是否为垃圾呢?
判断完就到回收垃圾算法了
配置元空间的初始值和最大值,设置堆空间的初始值和最大值。
-XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=256M -Xms256m -Xmx256m
具体大小设置还得看环境,回来填坑