一.java简介
先了解一些基础概念:
JDK(Java Development Kit):Java程序设计语言+Java虚拟机+Java API类库;
JRE(Java Runtime Environment):Java API类库中的的Java SE API子集+Java虚拟机
Java构成基础工具:java、javac、javadoc、apt、jar、javap、JPDA、JConsole、Java VisualVm
Java技术体系(按平台划分):
Java Card:支持小型程序(applet),java程序运行在小内存设备的平台,如智能卡。
Java ME(Micro Edition),旧称J2ME:包含JavaAPI精简集,是针对移动终端支持的平台。
Java SE(Standard Edition),旧称J2SE:提供完整Java 完整API核心,支持桌面应用(如:windows的桌面应用)的平台。
Java EE(Enterprise Edition),旧称J2EE:支持使用多层架构的企业应用(如ERP,CRM应用)的java平台,包含Java SE API,还有大量的扩展(以java.*为包名的包都是Java SE API的核心包,以java.*作为包名的是扩展,但也有部分扩展的API后面进入了核心包)和部署支持。
java的语言特性:
支持模块化开发与部署
支持混合语言开发,基于java虚拟机运行(各个语言基于JVM的实现版本)
支持多核并行
具有丰富的语法特性(各个版本的新增特性):(1.5后)自动装箱、泛型、动态注解、枚举、可变长参数、遍历循环),更多请查看
支持64位cpu架构
二.自动内存管理机制
java运行时数据区:方法区、虚拟机栈、本地方法栈、堆、程序计数器
程序计数器:本质上是线程说执行字节码的行号指示器,每个线程具有一个独立的程序计数器,处理分支,循环,跳转,异常处理,线程恢复等基础工作。
虚拟机栈:本质上是java方法执行的内存模型(每一个方法被调用直至执行完成的过程就是栈帧在虚拟机中从入栈到出栈的过程),每个线程私有一个栈,且每个栈的生命周期与线程相同。栈帧包含:局部变量表(基础数据类型及引用对象),操作栈,动态链表,方法出口等
本地方法栈:与虚拟机栈类比,唯一区别是本地方法栈是为虚拟机使用到的native方法服务。
Java堆(又称GC堆):本质上是几乎所有对象实例进行内存分配,被所有线程共享的一块内存区域。
方法区(非堆):本质上是用于存储已被虚拟机加载(注意不是实例)的类的信息,常量,静态变量,即时编译产生的代码等数据的被各个线程所共享的内存区域。包含:类的版本,字段,方法,接口,运行时常量池。
容易忽视的内存泄露原因:
某些对象生命命周期过长,持有状态时间过长
什么是引用
JDK 1.2以前:如果reference类型的数据存储值代表的是另一块内存的起始地址,称其为引用
JDK 1.2之后:引入四种引用类型
强引用: 使用“Object obj=new Object()”创建的实例为强引用 。只要强引用还在,垃圾回收器永远不会回收掉被引用的对象。
软引用:使用SoftReference类来实现的为软引用。系统在将要发生内存泄露异常之前,会把这些对象列进回收范围之内进行第二次回收。
弱引用:使用WeakReference类来实现的为弱引用。被弱引用关联的对象只能生存到下次垃圾回收之前(即当垃圾回收集器工作时,无论当前内存是否足够,都会回收只被弱引用关联的对象)。
虚引用:使用PhantomReference类来实现的为虚引用。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来去取得一个对象实例,但是回收时会收到一个系统通知。
判断对象是否存活的算法:
引用计数法:
给对象添加一个计数器,每被引用是计数器数值加1,没当引用失效时,数值减1,当任意时刻对象的计数器数值都为零时就是对象不再被使用。
优点:判定效率高
缺点:难以解决对象之间相互循环引用地问题
根搜索算法:
通过一系列“GC Root”的对象作为起点,往下搜索,走过的路径称为引用链,当一个对象没有任何引用链到达GC Root时(图的历遍),则此对象不再被使用。
优点:能准确有效地判定对象不再被使用问题
缺点:判定效率相对较低(涉及多个图的历遍)
方法区的特殊回收判定
回收对象:废弃的常量、无用的类(该类的所有实例都已经被回收,加载该类的ClassLoader已经被回收,该类对应的Java.lang.Class对象没有在任何地方被引用(即无法通过任何反射方法访问该类的方法))
满足条件的对象可以被回收,不意味马上回收。
垃圾回收算法
标记-清除算法:标记出所有需要回收的对象,完成后统一回收掉所用被标记的对象
优点:最基础,简单的算法
缺点:效率不高,标记清除后会产生大量碎片空间,不利于分配大内存对象(需要连续的内存空间)
复制算法:将可用内存划分为容量相对的两块区域,每次只使用一块。当该区域用完了就将存活的对象复制到另一块区域,然后把已经使用过的原来区域空间一次性清理掉。
优点:不用考虑内存碎片,实现简单高效
缺点:内存缩小为了原来的一半
老年代-新生代算法(商业虚拟机都采用此算法):类似于复制算法,不同的是内存空间不再等分,而是默认划分为8:1:1的Eden空间,Survivor空间和Survivor空间。每次只使用Eden和一块Survivor,回收时将Eden和Survivor存活的对象拷贝到空闲的Survivor空间,最后清理掉Eden和第一个使用的Survivor空间。
优点:不用考虑内存碎片,也更有效地利用了内存空间
缺点:可能存在特殊的情况,存活的对象太多,Survivor区域不够用
标记-整理算法:基于标记-清除算法,先标记可回收对象,但不是直接清除可回收对象,而是让所有存活的对象移动到一端,然后清理掉边界以外的内存。
优点:具有新生代-老年代算法的优点,又解决了Survivor区可能会不足的缺点
缺点:相对复杂,效率较低
分代收集算法:根据对象存活周期的不同将内存划分为几块,根据区域内存活周期的不同特点采用最合适的收集算法。
优点:综合了上面所有算法的优点
缺点:更加复杂,效率较低
内存分配与回收策略
对象优先在Eden区分配
大对象直接进入老年代(虚拟机害怕短命的大对象)
长期存活对象进入老年代(对象年龄计数器)
动态年龄对象判定:如果在Survivor空间中相同年龄的所有对象总和大于Survivor空间的一半,年龄大于或者等于该年龄的对象就可以进入老年代。
空间担保分配:
minor gc之前,虚拟机会检测 : 老年代最大可用的连续空间>新生代all对象总空间?
1、满足,minor gc是安全的,可以进行minor gc
2、不满足,虚拟机查看HandlePromotionFailure参数:
(1)为true,允许担保失败,会继续检测老年代最大可用的连续空间>历次晋升到老年代对象的平均大小。若大于,将尝试进行一次minor gc,若失败,则重新进行一次full gc。
(2)为false,则不允许冒险,要进行full gc(对老年代进行gc)。
新生代GC(Minor GC):指发生在新生代的垃圾收集动作(新生代具备朝生夕灭的特点),回收会很频繁,回收速度也快
老年代GC(Major GC/Full GC):指发生在老年代的垃圾回收动作(老年代相对稳定存在),Full GC 一般比Minor GC慢10以上。