理解Java中的内存泄漏,我们首先要清楚Java中的内存区域分配问题和内存回收的问题本文将分为三大部分介绍这些内容。

Java中的内存分配

Java中的内存区域主要分为线程共享的和线程私有的两大区域:

  • Java堆:在虚拟机启动时创建,是所有线程共享的一块内存区域。存放了所有的new出来的对象的实例和数组,对象的reference则在虚拟机栈上。
  • 方法区:与Java堆一样,是各个线程共享的内存区域,用于存储被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据
  • 虚拟机栈:每个方法在执行过程中都会创建一个栈帧,用来保存局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从开始执行到执行结束都对应着一个栈帧在虚拟机中从入栈到出栈的过程。我们通常说的栈内存就是指的是虚拟机栈的本地变量表部分,主要存储了基本数据类型和对象的引用。
  • 程序计数器:是线程执行字节码文件位置的指示器,用于线程切换后,再次切换回来能准确执行上次执行到的字节码文件的位置。
  • 本地方法栈:和虚拟机栈类似,只是记录的是Native方法。

介绍了Java的内存分配问题,通过一段代码来进行一下总结

public static void main(String[] args) {Animal animal = new Animal();}
publci class Animal{public static String address = "the earth"public Animal(){}
}
复制代码

在main方法中,我们new Animal(),首先,检查内存中是否加载了Animal.class,如果没有加载,则会先将Animal.class加载到方法区中,同时在方法区开辟一块内存区域,用于存储static 的"the earth"(这里更能清晰的理解static这个关键字,就是static修饰的变量是属于类的,而不是属于类的某一个对象的),随后在Java堆中开辟一块内存区域,用于存储new Animal()这个对象,在虚拟机栈中的animal会指向这个对象的地址。

Java中的内存回收机制

Java内存回收主要是指的是Java堆上的已经分配给对象的内存回收,判断Java堆上的内存是否被回收,主要是通过可达性分析算法:从一系列可以作为 GC Roots 的对象开始向下搜索,搜索走过的路径称为引用链,当GC回收时,一个对象没有通过引用链与任何GC Roots对象连接,则这个对象就可以被回收了。可作为GC Roots对象的有以下几种:

  • 虚拟机栈的本地变量表中引用的对象。
  • 方法区中静态属性和常量引用的对象。
  • 本地Native方法引用的对象。

需要注意的是GC Roots并不是一直可以作为GC Roots的,eg:

public void testGc(){Person person = new Person();
}
复制代码

根据GC Roots的定义,new Person()这个对象被person所引用,person在虚拟机栈中,所以new Person()这个对象是作为了GC Roots的,但是当这个testGc()方法执行完成,person释放内存,new Person()这个对象就没有其他的引用,就不再是GC Roots。

Java中的内存泄漏

内存泄漏需要和内存溢出区分开来,内存泄漏是指:部分内存我们不再需要了,但是虚拟机不能回收,泄漏过多就会造成内存溢出。就是部分对象我们已经用不上了,但是这些对象和GC Roots存在一定程度上的引用关系,导致不能被垃圾回收机制回收。

几种常见的内存泄漏

  • 非静态内部类的静态实例
public class InnerStactivity extends AppCompatActivity {private static Object ininerClass;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_inner_stactivity);}class InnerClass{}public void startInnerClass(){ininerClass = new InnerClass();}/*** 创建了InnerClass的静态实例引用* @param view*/public void createInner(View view) {startInnerClass();}
}
复制代码

当static Object ininerClass指向了newInnerClass()这个对象时,这个对象就可以作为了GC Roots,同时非静态的内部类会持有外部类的引用,InnerStactivity就会在GC Roots的引用链上,当我们需要离开这个InnerStactivity,并且不再需要这个InnerStactivity时,这个InnerStactivity并不能被回收。

  • 匿名内部类的静态实例

  • Handler内存泄漏

public class HandlerActivity extends AppCompatActivity {Handler mHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handler);mHandler= new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);show();}};}public void sendMessage(View view) {mHandler.sendEmptyMessageDelayed(1,1000*30);finish();}public void show(){}}
复制代码

可以看到我们这么写Android studio已经提示了警告,提示我们应该用static修饰handler对象,否则会造成内存的泄漏,这是不容易犯的错误。

Handler这么写之所以会出现内存泄漏是因为:Message会持有一个对Handler的引用,当这个Handler是非静态内部类的时候,又会持有一个对外部类的引用(比如Activity)。如果发送一条延时的Message,由于这个Message会长期存在于队列中,就会导致Handler长期持有对Activity的引用,从而引起视图和资源泄漏。当你发送一条延时的Mesaage,并且把这个Message保存在某些地方(例如静态结构中)备用,内存泄漏会变得更加严重。

我们现在都通过static修饰 Handler类,并通过弱引用来和当前界面的Activity交互,并在onDestory()中去除Handler的所有的消息来规避可能出现的内存泄漏。

public class HandlerImproveActivity extends AppCompatActivity {Handler mHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_handler);mHandler= new ImproveHandler(HandlerImproveActivity.this);}public void sendMessage(View view) {mHandler.sendEmptyMessageDelayed(1,1000*30);finish();}private static class ImproveHandler extends Handler{private WeakReference<HandlerImproveActivity> mActivity;public ImproveHandler(HandlerImproveActivity improveActivity) {this.mActivity = new WeakReference<HandlerImproveActivity>(improveActivity);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (mActivity != null && mActivity.get() != null) {mActivity.get().show();}}}public void show(){}@Overrideprotected void onDestroy() {super.onDestroy();if(mHandler != null){mHandler.removeCallbacksAndMessages(null);}}
}
复制代码
  • 未正确使用Context 在开发中会有大量的使用到Context的地方,如果不是必须使用到Activity的Context(例如Dialog就必须使用到activity的Context),则都可以使用getApplicationContext()来替代。
 AlertDialog alertDialog= new AlertDialog.Builder(this).create();Toast.makeText(getApplicationContext(), "", Toast.LENGTH_SHORT).show();
复制代码
  • 注册未取消: 在使用观察者模式的时候,在register后,完成时要即时unRegister监听器。在某个Activity界面使用Rxjava进行网络请求,在离开这个页面的时候一定要取消注册。
  • 资源对象未关闭: 资源对象比如Cursor、File等,不使用的时候应该关闭它们。把他们的引用置为null,而不关闭它们,往往会造成内存泄漏。因此,在资源对象不使用时,一定要确保它已经关闭,通常在finally语句中关闭,防止出现异常时,资源未被释放的问题。

Android Developer中关于如何管理内存的链接 developer.android.com/topic/perfo…

深入理解Java中的内存泄漏相关推荐

  1. 翻译 | 理解Java中的内存泄漏

    猪年第一篇译文,大家多多支持! 原文自工程师baeldung博客,传送门 1. 介绍 Java 的其中一个核心特点是经由内置的垃圾回收机制(GC)下的自动化内存管理.GC 默默地处理着内存分配和释放工 ...

  2. 介绍Java中的内存泄漏

    转载自  介绍Java中的内存泄漏 Java语言的一个关键的优势就是它的内存管理机制.你只管创建对象,Java的垃圾回收器帮你分配以及回收内存.然而,实际的情况并没有那么简单,因为内存泄漏在Java应 ...

  3. Java中关于内存泄漏分析和解决方案,都在这里了!

    作者:李序锴 www.jianshu.com/p/54b5da7c6816 最近正在熟悉Java内存泄漏的相关知识,上网查阅了一些资料,在此做个整理算是对收获的一些总结,希望能对各位有所帮助,有问题可 ...

  4. Java中关于内存泄漏出现的原因以及如何避免内存泄漏

    转账自:http://blog.csdn.net/wtt945482445/article/details/52483944 Java 内存分配策略 Java 程序运行时的内存分配策略有三种,分别是静 ...

  5. (转载)Java中关于内存泄漏出现的原因以及如何避免内存泄漏

    原文链接 Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题.内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实 ...

  6. 了解Java中的内存泄漏

    来源:SpringForAll社区 1. 简介 Java的核心优势之一是在内置垃圾收集器(简称GC)的帮助下实现自动内存管理.GC隐含地负责分配和释放内存,因此能够处理大多数内存泄漏问题. 虽然GC有 ...

  7. java中的内存泄漏

    ● 请问java中内存泄漏是什么意思?什么场景下会出现内存泄漏的情况? 考察点:内存泄漏 参考回答: Java中的内存泄露,广义并通俗的说,就是:不再会被使用的对象的内存不能被回收,就是内存泄露.如果 ...

  8. 如何在Java中创建内存泄漏?

    我刚刚接受采访,并被要求使用Java造成内存泄漏. 不用说,我对如何开始创建它一无所知. 一个例子是什么? 解决方案: 这是在纯Java中创建真正的内存泄漏(运行代码无法访问但仍存储在内存中的对象)的 ...

  9. Java 中发生内存泄漏 5 个场景以及解决方法

    前言 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和 Java 联系起来.在 Java 中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM ...

最新文章

  1. 【Java每日一题】20170113
  2. UVa1588 | 算法竞赛入门经典(第二版) 习题3-11 换低档装置
  3. LSGO软件技术团队2015~2016学年第十六周(1214~1220)总结
  4. * IO流递归拷贝一个文件夹 按源文件夹格式拷贝
  5. iptables规则书写总结
  6. iDRAC 7 Enterp license
  7. 机器学习从入门到创业手记-1.2 机器学习的概念
  8. Raspberry Pi 4 with Debian GNU/Linux 11 (bullseye)
  9. win10计算机组共享的打印机,Win10系统局域网共享打印机设置 共享大地Win10打印机的方法...
  10. Java多线程Zip压缩
  11. j-style pro android,三星J7 Pro官方安卓9台湾版固件rom包:BRI-J730GMDXU6CSF5
  12. 超详细:SwiftUI文本排版布局和动态字体缩放的奥秘
  13. ThinkPad T460S 拆解图 拆解图
  14. umi2升级到umi3
  15. SAS(八)SAS之DATA步--文件操作语句
  16. SAP ABAP 添加企业微信群机器人并调用 API 发送消息
  17. iOS定位原理以及纠偏的一些建议
  18. 【项目精选】智慧物业管理系统
  19. PHY--RSRP、RSRQ、RSSI和SINR
  20. ** (java:10104): WARNING **: Could not open X display (MobaXterm无法打开smartgit)

热门文章

  1. 刘锋 吕乃基:互联网中心化与去中心化之争
  2. 藏经阁计划发布一年,阿里知识引擎有哪些技术突破?
  3. 美国五大科技巨头的人工智能竞赛
  4. 抽成 30% 的苹果税是良心价?谷歌也学“坏”了
  5. 长见识!居然还有程序员考公指南这种东西?
  6. 她们,在字节跳动写代码
  7. Mac下firefox ssl_error_ssl_disabled
  8. 关于网易云音乐爬虫的api接口?
  9. SpringBoot项目在IntelliJ IDEA中实现热部署
  10. BZOJ-1192-[HNOI2006]鬼谷子的钱袋