主要是记录工作中出现的问题和网上的一些解决方案
一 java 内存分配
堆内存和栈内存
- 1 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。
- 2 堆是先进先出,而栈是先进后出
- 3 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享的。
- 4 堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
根据以上分析,我们得知:
一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配;堆内存用于存放由new创建的对象和数组。即堆主要用来存放对象的,栈主要是用来执行程序的。如图:
在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。
二 Java中垃圾产生的原因
- 引用变量是普通变量,定义时在栈中分配内存,引用变量在程序运行到作用域外释放。而数组&对象本身在堆中分配,即使程序运行到使用new产生数组和对象的语句所在地代码块之外,数组和对象本身占用的堆内存也不会被释放,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。这个也是java比较占内存的主要原因。
三 Java 垃圾回收机制
那么GC怎么能够确认某一个对象是不是已经被废弃了呢?Java采用了有向图的原理。例如大多程序从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),则认为这个(这些)对象不再被引用,可以被GC回收。
Vector v= new Vector(10);
for (int i=1;i<100; i++){
Object o=new Object();
v.add(o);
o=null;
}
垃圾回收的例子:
在这个例子中,代码栈中存在Vector对象的引用v和Object对象的引用o。在For循环中,我们不断的生成新的对象,然后将其添加到Vector对象中,之后将o引用置空。问题是当o引用被置空后,如果发生GC,我们创建的Object对象是否能够被GC回收呢?
答案是否定的。因为,GC在跟踪代码栈中的引用时,会发现v引用,而继续往下跟踪,就会发现v引用指向的内存空间中又存在指向Object对象的引用。也就是说尽管o引用已经被置空,但是Object对象仍然存在其他的引用,是可以被访问到的,所以GC无法将其释放掉。如果在此循环之后,Object对象对程序已经没有任何作用,那么我们就认为此Java程序发生了内存泄漏。
四 android 内存泄露
Android的内存管理与Java的内存管理相似。Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,也就是说每个应用程序都是在属于自己的进程中运行的。Android为不同类型的进程分配了不同的内存使用上限。
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的
为什么会出现内存不够用的情况呢?原因主要有两个:
- 由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。
- 保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制
五 内存泄露的几种类型
(一) Static变量的使用
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享(如private static ProgressDialog mDelMessProDialog;)。其生命周期取决于类的生命周期,即类的所有实例结束了,如果类还存在,其静态变量依然还存在,只有当类被卸载。静态变量才被销毁。
如: private static Acitvity mContext;//
那么如何有效避免这种引用的发生呢?
第一 尽量避免static成员变量引用资源耗费过多的实例,如Context.
第二 Context尽量使用Application Context,因为Application的Context生命周期较长,引用它不会出现内存泄漏问题。
第三 使用WeakReference代替强引用,比如可以使用WeakReference<Context> mContextRef;
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new MyThread().start();
}
private class MyThread extends Thread{
@Override
public void run() {
super.run();
//do somthing
}
}
}
我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当调用finish的时候Activity会销毁掉吗?
public class ThreadDemo extends Activity {
: private static final String TAG = "ThreadDemo";
: private int count = 0;
: private Handler mHandler = new Handler();
: private Runnable mRunnable = new Runnable() {
: public void run() {
: //为了方便 查看,我们用Log打印出来
: Log.e(TAG, Thread.currentThread().getName() + " " +count);
: //每2秒执行一次
: mHandler.postDelayed(mRunnable, 2000);
: }
: };
: @Override
: public void onCreate(Bundle savedInstanceState) {
: super.onCreate(savedInstanceState);
: setContentView(R.layout.main);
: //通过Handler启动线程
: mHandler.post(mRunnable);
: }
:}
@Override
protected void onDestroy() {
mHandler.removeCallbacks(mRunnable);
super.onDestroy();
}
private ImageView preview;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options);
preview.setImageBitmap(bitmap);
第三、巧妙的运用软引用(SoftRefrence)
- 资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。
- 程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在长时间大量操作的情况下才会复现内存问题
- 这样就会给以后的测试和问题排查带来困难和风险。
public View getView(int position, View convertView, ViewGroup parent) {
View view = new View();
//通过inflate等找到布局 然后findViewById等 设置各个显示的
itemreturn view;
}
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
//复用了回收的view 只需要直接作内容填充的修改就好了
} else {
view = new Xxx(...);
//没有供复用的view 按一般的做法新建view
}
return view;
}
即:当ListView滑动的过程中 会有item被滑出屏幕 而不再被使用 这时候Android会回收这个条目的view 这个view也就是这里的convertView在上面的做法中 当item被移除屏幕的时候 我们会重新new一个View给新显示的item_new 而如果使用了这个convertView 我们其实可以复用它 ,这样就省去了new View的大量开销
参考文档:
http://fluagen.blog.51cto.com/146595/78018
http://www.cnblogs.com/xiaoran1129/archive/2012/11/29/2794860.html
android内存泄露