java性能权威指南_java代码性能优化

java性能权威指南_java代码性能优化java语言中,jvm虽然会自动回收垃圾。但是像数组,对象,最好不用的设置为null;1.先说说数组:byte[]tbytes=newbyte[100];,当你不使用的时候,直接tbytes=null;gc会自动回收置为null的对象;或者,直接你可以再加上一句System.gc(),强制回收;2.

java性能权威指南_java代码性能优化"

内存泄漏 :   http://www.importnew.com/12961.html                http://www.importnew.com/12901.html

java 语言中,jvm虽然会自动回收垃圾。但是像数组,对象,最好不用的设置为null;

1.先说说数组:

byte[] tbytes = new byte[100];当你不使用的时候,直接tbytes = null;gc会自动回收置为null的对象;
或者,直接你可以再加上一句System.gc(), 强制回收; 
2.Java虽然由GC来回收内存,但也是存在泄露问题的,只是比C++小一点。 (1)与C++的比较 C++所有对象的分配和回收都需要由用户来管理。即需要管理点,也需要管理边。若存在不可达的点,无法在回收分配给那个点的内存,导致内存泄露。存在无用的对象引用,自然也会导致内存泄露。 Java由GC来管理内存回收,GC将回收不可达的对象占用的内存空间。所以,Java需要考虑的内存泄露问题主要是那些被引用但无用的对象—即指要管理边就可以。被引用但无用的对象,程序引用了该对象,但后续不会再使用它。它占用的内存空间就浪费了,如果存在对象的引用,这个对象就被定义为“活动的”,同时不会被释放。 (2)Java内存泄露处理 处理Java的内存泄露问题:确认该对象不再会被使用,接着典型的做法——把对象数据成员设为null 注意,当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。 List myList = new ArrayList(); for (int i=1;i<100; i++){
  
      Object  o=new Object();    myList.add(o); o=null; } 此时,所有的Object对象都没有被释放,因为变量myList引用这些对象。当myList后来不再用到,将之设为null,释放所有它引用的对象。之后GC便会回收这些对象占用的内存 (3)内存泄露检测 市场上已有几种专业检查Java内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit Profiler,JProbe Profiler,JinSight, Rational公司的Purify等。 在运行过程中,我们可以随时观察内存的使用情况,通过这种方式,我们可以很快找到那些长期不被释放,并且不再使用的对象。我们通过检查这些对象的生存周期,确认其是否为内存泄露。 3、java程序设计中有关内存管理的经验 1. 最基本的建议是尽早释放无用对象的引用。 A a = new A(); //应用a对象 a = null; //当使用对象a之后主动将其设置为空 …. 注:如果a 是方法的返回值,不要做这样的处理,否则你从该方法中得到的返回值永远为空,而且这种错误不易被发现、排除 2. 尽量少用finalize函数。它会加大GC的工作量。 3. 注意集合数据类型,包括数组、树、图、链表等数据结构,这些数据结构对GC来说,回收更为复杂。 4. 尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其自类的构造器时造成不必要的内存资源浪费。由于对象的创建是递归式的,也就是先调用超级类的构造,然后依次向下递归调用构造函数, 所以应该避免在类的构造函数中初始化变量,这样可以避免不必要的创建对象造成不必要的内存消耗.当然这里也就看出来接口的优势。 5. 尽量避免强制系统做垃圾内存的回收,增长系统做垃圾回收的最终时间 6. 尽量避免显式申请数组空间 7. 别用new Boolean() 在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolean封装传递的,大部分ORM也是用Boolean来封装boolean类型的,比如: ps.setBoolean("isClosed",new Boolean(true)); ps.setBoolean("isClosed",new Boolean(isClosed)); ps.setBoolean("isClosed",new Boolean(i==3)); 通常这些系统中构造的Boolean实例的个数是相当多的,所以系统中充满了大量Boolean实例小对象,这是相当消耗内存的。Boolean类实际上只要两个实例就够了,一个true的实例,一个false的实例。 Boolean类提供两了个静态变量: public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false); 需要的时候只要取这两个变量就可以了, 比如:ps.setBoolean("isClosed",Boolean.TRUE); 那么象2、3句那样要根据一个boolean变量来创建一个Boolean怎么办呢?可以使用Boolean提供的静态方法:Boolean.valueOf() ps.setBoolean("isClosed",Boolean.valueOf(isClosed)); ps.setBoolean("isClosed",Boolean.valueOf(i==3)); 因为valueOf的内部实现是:return (b ? TRUE : FALSE); 所以可以节省大量内存。相信如果Java规范直接把Boolean的构造函数规定成private,就再也不会出现这种情况了。 8. 别用new Integer 和Boolean类似,java开发中使用Integer封装int的场合也非常多,并且通常用int表示的数值通常都非常小。SUN SDK中对Integer的实例化进行了优化,Integer类缓存了-128到127这256个状态的Integer,如果使用Integer.valueOf(int i),传入的int范围正好在此内,就返回静态实例。这样如果我们使用Integer.valueOf代替new Integer的话也将大大降低内存的占用。如果您的系统要在不同的SDK(比如IBM SDK)中使用的话,那么可以自己做了工具类封装一下,比如IntegerUtils.valueOf(),这样就可以在任何SDK中都可以使用这种特性。 9. 不要用StringBuffer代替字符串相加 10. 不要过滥使用哈希表 有一定开发经验的开发人员经常会使用hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用HashMap缓存一些物料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、oscache等,这些项目都实现了FIFO、MRU等常见的缓存算法。 11. 避免过深的类层次结构和过深的方法调用。因为这两者都是非常占用内存的(特别是方法调用更是堆栈空间的消耗大户)。 12. 变量只有在用到它的时候才定义和实例化。 13. 共享静态存储空间 我们都知道静态变量在程序运行期间其内存是共享的,因此有时候为了节约内存工件,将一些变量声明为静态变量确实可以起到节约内存空间的作用。但是由于静态变量生命周期很长,不易被系统回收,所以使用静态变量要合理,不能盲目的使用.以免适得其反。 因此建议在下面情况下使用:变量所包含的对象体积较大,占用内存过多;变量所包含对象生命周期较长;变量所包含数据稳定;该类的对象实例有对该变量所包含的对象的共享需求.(也就是说是否需要作为全局变量)。
-----------------------------内存泄漏相关---------------------
 1、非静态内部类的静态实例容易造成内存泄漏 public class MainActivityextends Activity  {       static Demo sInstance = null;                @Override     public void onCreate(BundlesavedInstanceState)  {           super.onCreate(savedInstanceState);           setContentView(R.layout.activity_main);           if (sInstance == null)   {              sInstance= new Demo();           }       }       class Demo  {       void doSomething()  {                  System.out.print("dosth.");       }      }   }   上面的代码中的sInstance实例类型为静态实例,在第一个MainActivity act1实例创建时,sInstance会获得并一直持有act1的引用。当MainAcitivity销毁后重建,因为sInstance持有act1的引用,所以act1是无法被GC回收的,进程中会存在2个MainActivity实例(act1和重建后的MainActivity实例),这个act1对象就是一个无用的但一直占用内存的对象,即无法回收的垃圾对象。所以,对于lauchMode不是singleInstance的Activity, 应该避免在activity里面实例化其非静态内部类的静态实例。 2、activity使用静态成员 private static Drawable sBackground;     @Override     protected void onCreate(Bundle state) {         super.onCreate(state);         TextView label = new TextView(this);         label.setText("Leaks are bad");             if (sBackground == null) {             sBackground = getDrawable(R.drawable.large_bitmap);         }     label.setBackgroundDrawable(sBackground);           setContentView(label);     }    由于用静态成员sBackground 缓存了drawable对象,所以activity加载速度会加快,但是这样做是错误的。因为在android 2.3系统上,它会导致activity销毁后无法被系统回收。 label .setBackgroundDrawable函数调用会将label赋值给sBackground的成员变量mCallback。 上面代码意味着:sBackground(GC Root)会持有TextView对象,而TextView持有Activity对象。所以导致Activity对象无法被系统回收。 下面看看android4.0为了避免上述问题所做的改进。 先看看android 2.3的Drawable.Java对setCallback的实现:     public final void setCallback(Callback cb){         mCallback = cb; } 再看看android 4.0的Drawable.Java对setCallback的实现:     public final void setCallback(Callback cb){         mCallback = newWeakReference<Callback> (cb); } 在android 2.3中要避免内存泄漏也是可以做到的, 在activity的onDestroy时调用 sBackgroundDrawable.setCallback(null) 以上2个例子的内存泄漏都是因为Activity的引用的生命周期超越了activity对象的生命周期。也就是常说的Context泄漏,因为activity就是context 想要避免context相关的内存泄漏,需要注意以下几点: 不要对activity的context长期引用(一个activity的引用的生存周期应该和activity的生命周期相同) 如果可以的话,尽量使用关于application的context来替代和activity相关的context 如果一个acitivity的非静态内部类的生命周期不受控制,那么避免使用它;正确的方法是使用一个静态的内部类,并且对它的外部类有一WeakReference,就像在ViewRootImpl中内部类W所做的那样。 3、使用handler时的内存问题 我们知道,Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。 所以正确处理Handler等之类的内部类,应该将自己的Handler定义为静态内部类。 HandlerThread的使用也需要注意:   当我们在activity里面创建了一个HandlerThread,代码如下: public classMainActivity extends Activity  {       @Override       public void onCreate(BundlesavedInstanceState)  {           super.onCreate(savedInstanceState);           setContentView(R.layout.activity_main);           Thread mThread = newHandlerThread("demo", Process.THREAD_PRIORITY_BACKGROUND);            mThread.start();   MyHandler mHandler = new MyHandler( mThread.getLooper( ) );   …….   }       @Override       public void onDestroy()  {       super.onDestroy();       }   }   这个代码存在泄漏问题,因为HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了activity生命周期,当横竖屏切换,HandlerThread线程的数量会随着activity重建次数的增加而增加。应该在onDestroy时将线程停止掉:mThread.getLooper().quit(); 另外,对于不是HandlerThread的线程,也应该确保activity消耗后,线程已经终止,可以这样做:在onDestroy时调用mThread.join(); 4、注册某个对象后未反注册 注册广播接收器、注册观察者等等,比如: 假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。   但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被GC回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。 虽然有些系统程序,它本身好像是可以自动取消注册的(当然不及时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉 5、集合中对象没清理造成的内存泄露   我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,如果没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。 比如某公司的ROM的锁屏曾经就存在内存泄漏问题: 这个泄漏是因为LockScreen每次显示时会注册几个callback,它们保存在KeyguardUpdateMonitor的ArrayList<InfoCallback>、ArrayList<SimStateCallback>等ArrayList实例中。但是在LockScreen解锁后,这些callback没有被remove掉,导致ArrayList不断增大, callback对象不断增多。这些callback对象的size并不大,heap增长比较缓慢,需要长时间地使用手机才能出现OOM,由于锁屏是驻留在system_server进程里,所以导致结果是手机重启。 6、资源对象没关闭造成的内存泄露   资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于Java虚拟机内,还存在于Java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该立即调用它的close()函数,将其关闭掉,然后再置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。   程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在长时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。 7、一些不良代码成内存压力 有些代码并不造成内存泄露,但是它们或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存,对内存的回收和分配造成很大影响的,容易迫使虚拟机不得不给该应用进程分配更多的内存,增加vm的负担,造成不必要的内存开支。 7.1Bitmap使用不当     第一、及时的销毁。   虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过Java堆的限制。因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。     第二、设置一定的采样率。  有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码: private ImageView preview;     BitmapFactory.Options options = newBitmapFactory.Options();     options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一     Bitmap bitmap =BitmapFactory.decodeStream(cr.openInputStream(uri), null, options); preview.setImageBitmap(bitmap);    第三、巧妙的运用软引用(SoftRefrence) 有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。如下: SoftReference<Bitmap>  bitmap_ref  = new SoftReference<Bitmap>(BitmapFactory.decodeStream(inputstream));    ……   if (bitmap_ref .get() != null)             bitmap_ref.get().recycle();   7.2,构造Adapter时,没有使用缓存的 convertView   以构造ListView的BaseAdapter为例,在BaseAdapter中提共了方法:   public View getView(intposition, View convertView, ViewGroup parent)   来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。   由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。ListView回收list item的view对象的过程可以查看:   android.widget.AbsListView.Java--> void addScrapView(View scrap) 方法。 public View getView(int position, View convertView, ViewGroupparent) {     View view = newXxx(...);      return view;     }   修正示例代码: public View getView(intposition, View convertView, ViewGroup parent) {   View view = null;   if (convertView != null){   view = convertView;   populate(view, getItem(position));   } else {   view = new Xxx(...);   }   return view;   }   7.3、不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用hashtable ,vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次 new 之后又丢弃。 
7.4、如何退出Activity?如何安全退出已调用多个Activity的Application? 
  对于单一Activity的应用来说,退出很简单,直接finish()即可。当然,
也可以用killProcess()和System.exit()这样的方法。 
对于多个activity,1、记录打开的Activity:每打开一个Activity,就记录
下来。在需要退出时,关闭每一个Activity即可。2、发送特定广播:在需要结
束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。3、递
归退出:在打开新的Activity时使用startActivityForResult,然后自己加标
志,在onActivityResult中处理,递归关闭。为了编程方便,最好定义一个
Activity基类,处理这些共通问题。 
在2.1之前,可以使用ActivityManager的restartPackage方法。 
它可以直接结束整个应用。在使用时需要权限
android.permission.RESTART_PACKAGES。 
注意不要被它的名字迷惑。 
可是,在2.2,这个方法失效了。在2.2添加了一个新的方法,killBackground Processes(),
需要权限android.permission.KILL_BACKGROUND_PROCESSES。可惜的是,它和2.2
的restartPackage一样,根本起不到应有的效果。 
另外还有一个方法,就是系统自带的应用程序管理里,强制结束程序的方法,
forceStopPackage()。它需要权限android.permission.FORCE_STOP_PACKAGES。并且
需要添加android:sharedUserId="android.uid.system"属性。同样可惜的是,该方法是非公
开的,他只能运行在系统进程,第三方程序无法调用。 
因为需要在Android.mk中添加LOCAL_CERTIFICATE := platform。 
而Android.mk是用于在Android源码下编译程序用的。 
从以上可以看出,在2.2,没有办法直接结束一个应用,而只能用自己的办法间接办到。 
现提供几个方法,供参考: 
1、抛异常强制退出: 
该方法通过抛异常,使程序Force Close。 
验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。 
2、记录打开的Activity: 
每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。 
3、发送特定广播: 
在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。 
4、递归退出 
在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult
中处理,递归关闭。 
除了第一个,都是想办法把每一个Activity都结束掉,间接达到目的。但是这样做同样不完
美。你会发现,如果自己的应用程序对每一个Activity都设置了nosensor,在两个Activity
结束的间隙,sensor可能有效了。但至少,我们的目的达到了,而且没有影响用户使用。
为了编程方便,最好定义一个Activity基类,处理这些共通问题。 

今天的文章java性能权威指南_java代码性能优化分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/58179.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注