JVM–基础–03–内存区域–堆
1、结构图
2、堆
- 堆中存的是对象:对象的大小是不可估计的,或者说是可以动态变化的
- 栈中存的是基本数据类型或者对象引用:一个对象引用大小是4btye。
2.1、特征
- 几乎所有的对象都存放在堆上。
- 堆是完全自动化管理的,通过垃圾回收机制,垃圾对象会被自动清理。
- 可能出现 OutOfMemoryError 异常。
2.2、栈到堆的关联过程
2.3、案例
SimpleHeap实例本身分配在堆中,描述SimpleHeap类的信息存放在方法区,main函数中的s1 s2局部变量存放在java栈上,并指向堆中两个实例。
3、内存溢出
当新建对象的时候,JVM没有内存可分配,且GC后还是没有内存可分配,那就会内存溢出
3.1、堆溢出代码案例
public class TestHeap {
// 分配128Kb的内存给数组,方便堆内存溢出
byte[] bis = new byte[128 * 1024];
public static void main(String[] args) {
List<TestHeap> list = new ArrayList<>();
//增加堆内存
while (true) {
list.add(new TestHeap());
}
}
}
结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.fei.zhou.day1.TestHeap.<init>(TestHeap.java:8)
at com.fei.zhou.day1.TestHeap.main(TestHeap.java:14)
3.2、内存溢出的场景
- 内存中加载的数据量过于庞大,如一次从数据库取出过多数据
- 集合类中对对象的引用,使用完后未清空,使得JVM不能回收
- 代码中存在死循环或循环产生过多对象
3.3、怎么避免
- 修改JVM启动参数,直接增加内存。
- 检查错误日志,查看"OutOfMemory"错误前是否有其它异常或错误
- 对代码进行走查和分析
3.3.1、对代码进行走查和分析,
- 检查代码中是否有死循环或递归调用。
- 检查是否有大循环产生新对象实体。
- 检查对数据库查询中,是否有一次获得全部数据的查询,而没有使用分页。
- 检查List、MAP等集合对象是否有使用完后,未清除的问题。
- List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。
- 尽早释放无用对象的引用
- 使用临时变量的时候,让引用变量在退出活动域后,自动设置为 null ,暗示垃圾收集器来收集该对象,防止发生内存泄露。
- 避免大量使用使用
String
- 尽量少用静态变量,因为静态变量是全局的, GC 不会回收的
- 避免集合中创建大对象
- 因为 JVM 会突然需要大量内存,这时必然会触发 GC 优化系统内存环境
- 尽量运用对象池技术以提高系统性能
- 不要在经常调用的方法中创建对象
3.3.2、避免大量使用使用String
避免大量使用使用String
,因为每一个 String 对象都得独立占用内存一块区域,所有应使用 StringBuffer
String str = "aaa";
String str2 = "bbb";
String str3 = str + str2;// 假如执行此次之后 str ,str2 以后再不被调用 , 那它就会被放在内存中等待 Java 的 gc 去回收 , 程序内过多的出现这样的情况就会报上面的那个错误 .
3.3.3、尽量运用对象池技术以提高系统性能
生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏。
例如:大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
3.3.3、不要在经常调用的方法中创建对象
可以适当的使用hashtable, vector创建一组对象容器,然后从容器中去取那些对象,而不用每次new
之后又丢弃