下图是Java代码编译的详细流程(即,javac的执行过程),了解便可,通常只要知道java文件是经过javac命令编译成class文件,再经过java命令运行的就能够了,如:java
javac Hello.java
java Hello
复制代码
java中的内存管理指的是下图中“内存空间”部分的内存操做。算法
做用:存放java方法执行时全部的数据。 组成:由栈帧组成,一个栈帧表明一个方法的执行。spa
Java栈帧:每一个方法从调用到执行完成就对应一个栈帧在虚拟机栈中入栈到出栈。它描述了一个方法的局部变量表、栈操做数、动态连接、方法出口。线程
与Java栈区基于一致。指针
做用:本地方法栈是专门为native方法服务的。code
存储被虚拟机加载的类信息、常量、静态常量、即时编译器编译后等数据(这些数据在程序启动后会永远占据内存)。cdn
做用:全部经过new建立的对象的内存都在堆中分配。 特色:是虚拟机中最大的一块内存,是GC要回收的部分。对象
对于堆区,其内存结构还有些不同的地方,先看下图:blog
简单来讲,堆区分为新生代(Young Generation)与老年代(Old Generation),程序在创始对象时,对象会先被分配到新生代中,当新生代区内存不足时,JVM会经过必定的算法规则将新生代中的对象转移至老年代中,当新生代与老年代都没有足够的内存空间时,JVM就会抛出OOM异常。内存
在内存建立对象的同时,会为它建立一个引用记数器,并将引用记数器加1,每次有引用引用到此对象时,记数器就会累计加1,而当其中一个引用销毁时,记数器就会减1,当引用记数器为0时,说明该对象已是垃圾对象,下次gc时,对象就会被回收了。
弊端: 对象A与对象B互相引用时,这2个对象的引用记数器永远是正数,当这2个对象都没有被其余对象所引用时(对象不可达),会由于它们的引用记数器不为0致使它们不会被gc回收。
也称为根搜索算法。把程序全部的引用关系看作是一张图(有向图),从GC Root节点开始寻找全部的引用节点,当全部的引用节点寻找完毕以后,剩余的节点被认为是没有引用的节点,即不可达的节点,就是垃圾对象。
上图中ObjD、E、F由于没有路径可达,因此是垃圾对象。
java中的引用类型有4种:强引用、软引用、弱引用、虚引用。其中,强引用和弱引用在开发中最经常使用。
弱引用的建立
// 强引用
Object obj = new Object();
// 弱引用,此时obj与wf都引用了Object对象
WeakReference<Object> wf = new WeakReference<Object>(obj);
// 断开强引用,此时只有wf引用这个Object对象
obj = null;
// 通用弱引用获取Object对象(可能为null)
wf.get();
复制代码
在使用wf.get()时,要判断获取到的对象是否为null,由于弱引用不会阻止对象的回收。
从根集合遍历全部的引用,上图中,根集合引用了A,A引用了C,B是不可达的对象引用,在扫描阶段中,B会被标记为垃圾对象,当垃圾回收机制执行时,会直接将B对象置为空,此时内存块中就只剩下A、C对象引用,B就被垃圾回收给回收掉了。 优势:不须要进行对象的移动,仅对不存活的对象进行处理,在存活对象比较多的状况下极为高效。 缺点:因为标记-清除算法会直接回收掉不存活的对象,会形成内在碎片,不利于后续对象的分配
从根集合开始遍历,上图中,遍历到A时是可达的,就把A复制到另外一块空闲的内存中,继续遍历,发现B不可达,直接跳过,日后,发现C可达,就把C一样地复制到这块空闲内存中,等全部复制都处理完时,把原来的内存空间清空,只保留复制后的这块内存空间。 优势:当存活对象比较少时,极为高效,且不会有产生内存碎片。 缺点:须要一块内存做为交换空间来进行对象的移动。
从根集合开始遍历,经过对整个内存区的扫描,将可回收对象扫描出来,上图中,到了第二阶段,就将B标记为可回收对象,到了第三个阶段,直接扫描并消除内存中被标记的对象,同时,在回收不存活对象占用的空间时,会将内存中全部存活对象往左端空闲处移动,并更新对应的指针。
这三种算法各有优劣,JVM在处理垃圾时会整合去使用,并非只使用其中某个算法。当内存中存活的对象比较少时,采用“复制算法”去处理垃圾对象;当内存中存活的对象比较多时,会采用“标记-整理算法”或“标记-清除算法”去处理垃圾对象。