目录
参考链接
理解Java的强引用、软引用、弱引用和虚引用
java的内存: 栈区、堆区、静态区/方法区
1、栈区:由编译知器自动分配释放,存放函数的参数值、局部变量的值等、基本类型的变量,例如int a=3中的a、对象的引用变量,例如Thread t=new Thread()中的t、具体方法执行结束之后,系统自动释放JVM内存资源。
2、堆区:一般由程序员分配释放,存放由new创建的对象和数组,jvm不定时道查看这个对象,如果没有引用指向这个对象就内回收。
3、静态区/方法区:存放全局变量,静态变量和字符串常量,不释放和整个应用的生命周期一样。
堆内存和栈内存,两者的区别?
-
引用变量是普通变量,定义时在栈内存中分配,引用变量在程序运行到作用域外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用new产生数组和对象的语句所在的代码块之外,数组和对象本身占用的堆内存也不会被释放。数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉,这个也是Java比较占内存的主要原因。实际上,栈中的引用变量指向堆内存中的变量,这就是Java中的指针。
-
通俗来讲,堆是用来存放对象的,而栈是用来执行程序的。
-
jvm只有一个堆区(heap),被所有线程共享;
每个线程包含一个栈区(stack),每个栈中的数据都是私有的,其他的栈不能访问,但是同一个栈中变量是共享的;分为3个部分:基本类型变量区,执行环境上下文,操作指令区。
为什么要有堆和栈?这样设计有什么好处?
- Java自动管理堆和栈,程序员不能直接地设置栈和堆。
- Java的堆是一个运行时数据区。堆是由JVM的垃圾回收器自动管理的。堆的优势是可以在程序运行时,动态地分配内存
大小,但是正是由于这个原因,它的存取速度较慢。 - 栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小和生存期是必须确定的,缺乏灵活性。
栈有一个很重要的特性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用时
指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。
内存泄漏分析:
强引用
Object obj = new Object(); //只要obj还指向Object对象,Object对象就不会被回收
obj = null; //手动置null,帮助垃圾收集器回收此对象
单例的内存泄漏
单例的内存泄露 当Android想要释放Activtity的时候 有单例(单例是static类型的 他的生命周期和整个程序一样)使用了Activity的context导致单例无法释放,推荐使用Application的Context
内部类
非静态内部类持有外部类实例的强引用。
如果我们在内部类创建一个静态的实例,该实例的生命周期和程序一样长,导致了该实例一直持有外部类的引用 导致该外部类的内存一直无法释放。
class test{
private static final TAG = "";
}
解决方法:
1.将内部类更改为静态内部类
static class test{
private static final TAG = "";
}
2.避免静态变量
class test{
private final TAG = "";
}
匿名内部类
如果这个非静态内部类实例内部执行耗时任务期间,Activity不幸被销毁了,就会导致外围对象不会被回收,从而导致内存泄漏
AsyncTask
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}.execute();
}
解决方法
private static class NimbleTask extends AsyncTask<Void, Void, Void> {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}
void startAsyncTask() {
new NimbleTask().execute();
}
Handler
// 容易造成内存泄漏的写法:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//...刷新页面
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
解决方法1: 使用静态内部类并且弱引用Activity
// 修复内存泄漏的方法:
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
// activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
解决方式2: 将Handler 置null
public class HandlerTestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
}
public void testHandler(View view) {
test();
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
mHandler = null;
}
private Message mMessage = new Message();
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
startActivity(new Intent(HandlerTestActivity.this, TestActivity.class));
return false;
}
});
private void test() {
new Thread(
new Runnable() {
@Override
public void run() {
// Handler内存泄露测试(假象)
SystemClock.sleep(3000);
mMessage.what = 3;
if(mHandler!=null){
mHandler.sendMessage(mMessage);
}
}
}
).start();
}
}
Thread
void testThread() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
解决方法
private Thread thread;
@Override
public void onDestroy() {
super.onDestroy();
if (thread != null) {
thread.interrupt();
}
}
void spawnThread() {
thread = new Thread(new Runnable() {
@Override
public void run() {
while (!isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
bitmap资源未释放
建议手动调用recycle()方法,释放其Native内存:
if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
bitmap = null;
}