当前位置:网站首页>JVM内存泄漏和内存溢出的原因
JVM内存泄漏和内存溢出的原因
2022-08-09 12:22:00 【Java技术债务】
目录
1 概念
- 内存泄漏: 分配出去的内存没有被回收回来,失去对内存区域的控制,造成资源的浪费,比如:new出来了对象并没有引用,垃圾回收器不会回收他,造成内存泄漏
- 内存溢出: 程序所需要的内存超出了系统所能分配的内存。2 分析内存溢出可能出现的地方 从 Java代码的运行过程来看,有三个区域会发生 OOM,它们分别是:Metaspace、Java 虚拟机栈、堆内存。 Java栈
虚拟机栈,每执行一个方法都会有一个栈帧入栈,栈帧中包含参数、局部变量、返回值地址等信息。如果代码层次太深,不断有方法入栈却没有出栈,Java虚拟机栈就会OOM。
- 虚拟机中的栈内存也是有限的,我们调用方法的时候会创建一个栈帧,紧接着方法入栈。如果一个线程一直调用方法入栈,栈内存终归是要满的,此时线程的栈中就会发生 OOM。
- 发生这种情况一般就是代码除了问题,比如写了个递归调用,和 Metaspace 的内存溢出一样,也很少发生。
- 如果在单线程的情况下,无论是栈帧太大还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机抛出的是StackOverflowError异常。
- 如果在多线程下,不断地建立线程可能会产生OutOfMemoryError异常。
Metaspace
保存类的基本信息,如果加载太多类就会 OOM
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。
回收废弃常量与回收 Java 堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串”abc”已经进入了常量池中,但是当前系统没有任何一个 String 对象是叫做”abc”的,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个”abc”常量就会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
注意:在大量使用反射、动态代理、CGLib 等 ByteCode 框架、动态生成 JSP 以及 OSGi这类频繁自定义 ClassLoader 的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。
堆
堆中创建的对象过多就会触发GC,GC 的速度赶不上新建对象的速度也会发生 OOM。
- 高并发场景下,请求量太大,创建了大量新的对象,且这些都是有用的、存活的。堆中无法放入更多对象就会导致堆内存溢出
- 内存泄漏问题,长生命周期的对象引用了大量短生命周期的对象,没有及时取消对它们的引用,导致 GC 无法回收这些理应被回收的对象,就导致了堆内存溢出
- Java堆中只会产生OutOfMemoryError异常。
注意:类需要同时满足下面 3 个条件才能算是“无用的类”
- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
- 加载该类的 ClassLoader 已经被回收。
- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样,不使用了就必然会回收。
注意:方法区溢出方法区中只会产生OutOfMemoryError异常。
2 分析内存泄漏的原因
原因:
长生命周期对象持有短生命周期对象的引用可能会引起内存泄漏
1、静态集合类:容器使用时引起的内存泄漏
HashMap、Vector等很容易出现内存泄漏, 集合被定义成静态的时候,由于它们的生命周期跟应用程序一样长 他们引用的所有对象Object不能被释放,如果将Object对象置为null,也还是会被Vector引用,可以将Vector对象置为null,切出对静态集合类的引用。
Vector vector = new Vector();
for (int i = 1; i<100; i++) {
Object object = new Object();
vector.add(object);
object = null;
}
//这样会造成内存泄漏
//...对vector的操作
//...与vector无关的其他操作这样会造成短暂的内存泄漏,method方法结束后被回收,
//...对vector的操作
vector = null;
//...与vector无关的其他操作
2、各种连接时:未正确使用close()方法导致的内存泄漏
各种IO或者数据库连接时,最后都需要close()释放对象,这样也是长对象引用短对象,造成的内存泄漏。
SessionFactory factory = new SessionFactory();
try {Session session = factory.connect();
} finally{
session.close();
}
这里必须用close关闭连接,因为SessionFactory是长对象,session是短对象。
3、外部模块的引用
调用外部模块的时候,也应该注意防止内存泄漏。如模块A调用了外部模块B的一个方法,如:public void register(Object o)。这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了去除引用的方法,如unregister()
4、单例模式
使用单例模式的时候也有可能导致内存泄漏。因为单例对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象(生命周期比较短)的引用,那么这个外部对象就不能被回收,而导致内存泄漏。如果这个外部对象还持有其它对象的引用,那么内存泄漏会更严重
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}
这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:
- 如果此时传入的是 Application 的 Context,因为 Application 的生命周期就是整个应用的生命周期,所以这将没有任何问题。
- 如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会被回收,这就造成泄漏了。
正确的方式应该改为下面这种方式:
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
// 使用Application 的context
this.context = context.getApplicationContext();
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}
或者这样写,连 Context 都不用传进来了: 在你的 Application 中添加一个静态方法,getContext() 返回 Application 的 context,
...
context = getApplicationContext();
...
//获取全局的context - - >return返回全局context对象
public static Context getContext(){
return context;
}
public classAppManager{
private static AppManager instance;
private Context context;
private AppManager() {
// 使用Application 的context
this.context = MyApplication.getContext();
}
public static AppManager getInstance() {
if (instance != null) {
instance = new AppManager();
}
return instance;
}
}
如何解决以及监控JVM内存、CPU的情况,请关注下篇文章。。。
边栏推荐
- 如何修改data work上jdbc驱动的版本
- HAproxy: load balancing
- The FFmpeg library is configured and used on win10 (libx264 is not configured)
- Go-based web access parameters
- The latest interview summary in 20022 brought by Ali senior engineer is too fragrant
- Intranet penetration tool ngrok usage tutorial
- 曲鸟全栈UI自动化教学(八):框架代码讲解和进一步优化
- 又有大厂员工连续加班倒下/ 百度搜狗取消快照/ 马斯克生父不为他骄傲...今日更多新鲜事在此...
- 智驾科技完成C1轮融资,此前2轮已融4.5亿元
- Fragment中嵌套ViewPager数据空白页异常问题分析
猜你喜欢
The latest interview summary in 20022 brought by Ali senior engineer is too fragrant
Flutter入门进阶之旅(五)Image Widget
#物联网征文#小熊派设备开发实战
世界第4疯狂的科学家,在103岁生日那天去世了
罗振宇折戟创业板/ B站回应HR称用户是Loser/ 腾讯罗技年内合推云游戏掌机...今日更多新鲜事在此...
基于CAP组件实现补偿事务与幂等性保障
LeetCode #101. Symmetric Binary Tree
数据挖掘-05
MySQL principle and optimization of Group By optimization techniques
The grep command Shell regular expressions, the three musketeers
随机推荐
Do you know the difference between comments, keywords, and identifiers?
Flutter入门进阶之旅(五)Image Widget
Byte Qiu Zhao confused me on both sides, and asked me under what circumstances would the SYN message be discarded?
Flutter入门进阶之旅(八)Button Widget
手写大根堆
Adalvo收购其首个品牌产品Onsolis
1-hour live broadcast recruitment order: industry big names share dry goods, and enterprise registration opens丨qubit·viewpoint
WeChat payment development process
The core key points of microservice architecture
Common gadgets of Shell (sort, uniq, tr, cut)
ViewPager fragments of nested data blank page abnormal problem analysis
26. Pipeline parameter substitution command xargs
位图与位运算
曼城推出可检测情绪的智能围巾,把球迷给整迷惑了
Scala Advanced (7): Collection Content Summary (Part 1)
h264 protocol
Flutter入门进阶之旅(四)文本输入Widget TextField
Flutter入门进阶之旅(十)Dialog&Toast
张朝阳对话俞敏洪:一边是手推物理公式,一边是古诗信手拈来
Flutter Getting Started and Advanced Tour (8) Button Widget