一、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 ME(Micro Edition),旧称J2ME:包含Java API精简集,针对移动终端支持的平台。
- Java SE(Standard Edition),旧称J2SE:提供完整Java核心API,支持桌面应用(如Windows桌面应用)的平台。
- Java EE(Enterprise Edition),旧称J2EE:支持使用多层架构的企业应用(如ERP、CRM)的Java平台,包含Java SE API及大量扩展。
Java的语言特性
- 支持模块化开发与部署
- 支持混合语言开发(基于JVM实现)
- 支持多核并行
- 丰富的语法特性(1.5后:自动装箱、泛型、动态注解、枚举、可变长参数、遍历循环等)
- 支持64位CPU架构
二、自动内存管理机制
Java运行时数据区
- 程序计数器:线程所执行字节码的行号指示器,每个线程独立,用于分支、循环、跳转、异常处理、线程恢复等。
- 虚拟机栈:Java方法执行的内存模型(每个方法从调用到完成对应一个栈帧的入栈到出栈),线程私有,生命周期与线程相同。栈帧包含局部变量表、操作栈、动态链接、方法出口等。
- 本地方法栈:与虚拟机栈类似,为Native方法服务。
- Java堆(GC堆):几乎所有对象实例分配内存的地方,线程共享。
- 方法区(非堆):存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等,线程共享。包含运行时常量池。
容易忽视的内存泄露原因:某些对象生命周期过长,持有状态时间过长。
引用类型
JDK 1.2之后引入四种引用:
- 强引用:
Object obj = new Object(),只要强引用存在,垃圾回收器不会回收。 - 软引用:
SoftReference类实现,内存不足时回收。 - 弱引用:
WeakReference类实现,下次垃圾回收时无论内存是否充足都会回收。 - 虚引用:
PhantomReference类实现,无法通过虚引用获取对象实例,回收时会收到系统通知。
判断对象是否存活
- 引用计数法:为对象添加计数器,引用时+1,失效时-1,为0时回收。
优点:效率高。
缺点:无法解决循环引用。 - 根搜索算法(可达性分析):以“GC Roots”为起点向下搜索,若对象无任何引用链到达GC Roots,则判定为可回收。
优点:准确解决循环引用。
缺点:效率相对较低。
可作为GC Roots的对象包括:虚拟机栈引用的对象、方法区静态属性引用的对象、方法区常量引用的对象、Native方法引用的对象等。
方法区的回收
回收废弃的常量、无用的类(需同时满足:所有实例被回收、ClassLoader被回收、Class对象无任何引用)。
垃圾回收算法
| 算法 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 标记-清除 | 标记可回收对象,统一清除 | 基础简单 | 效率低,产生内存碎片 |
| 复制算法 | 将内存分为两块,只使用一块,存活对象复制到另一块后整体清理 | 无碎片,实现简单高效 | 内存利用率低(一半) |
| 标记-整理 | 标记后让存活对象向一端移动,再清理边界外内存 | 无碎片,适合老年代 | 效率相对较低 |
| 分代收集 | 根据对象存活周期分代,不同代使用不同算法 | 综合优化 | 实现复杂 |
商业虚拟机中,新生代常用复制算法(Eden:Survivor=8:1:1),老年代常用标记-清除或标记-整理。
内存分配与回收策略
-
对象优先在Eden区分配
-
大对象直接进入老年代
-
长期存活对象进入老年代(年龄计数器)
-
动态年龄判定:Survivor空间中相同年龄对象总和超过Survivor空间一半,大于等于该年龄的对象直接进入老年代。
-
空间担保分配:Minor GC前,若老年代连续空间大于新生代所有对象总空间则安全;否则检查
HandlePromotionFailure参数,决定是否进行Full GC。 -
Minor GC(新生代GC):频繁,回收速度快。
-
Full GC / Major GC(老年代GC):速度比Minor GC慢10倍以上。