關(guān)于是否可以try catch OutOfMemoryError的討論

關(guān)于是否可以try catch OutOfMemoryError的討論

目錄

[TOC]

問題由來

這是一家公司的面試題目,感覺有點意思,所以面試回來準(zhǔn)備測試下什么情況

問題論點

對于這個問題,主要討論兩種OutOfMemory可能性,一種是突然使用了大量內(nèi)存,比如加載了特別巨大的圖片,第二是內(nèi)存泄漏.

然后還有個問題是,一旦發(fā)生OOM,引發(fā)OOM的操作是否會成功,如果會成功賦值是否會成功呢?理論上操作和賦值都不會成功的,但是我覺得有必要嘗試一下.

構(gòu)建測試代碼

那么針對問題構(gòu)建測試代碼如下

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/out"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:text="變量在try外面"
        android:textSize="15sp" />

    <Button
        android:id="@+id/in"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:text="變量在try里面"
        android:textSize="15sp" />

    <Button
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:text="添加數(shù)組"
        android:textSize="15sp" />

    <Button
        android:id="@+id/action"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:text="其他正常操作"
        android:textSize="15sp" />

    <Button
        android:id="@+id/gc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:text="垃圾回收"
        android:textSize="15sp" />

    <TextView
        android:id="@+id/left"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:padding="10dp"
        android:textSize="15sp" />
</LinearLayout>

MainActivity.java

package com.yxf.trytocatchoutofmemory;

import android.app.ActivityManager;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
    private ArrayList<byte[]> mArrayList = new ArrayList<byte[]>();

    private Button mOutButton, mInButton, mAddButton, mActionButton, mGcButton;
    private TextView mLeftMemoryView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mOutButton = findViewById(R.id.out);
        mInButton = findViewById(R.id.in);
        mAddButton = findViewById(R.id.add);
        mActionButton = findViewById(R.id.action);
        mGcButton = findViewById(R.id.gc);
        mLeftMemoryView = findViewById(R.id.left);
        mOutButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                byte[] values = null;
                try {
                    values = new byte[1024 * 1024 * 1024];
                } catch (OutOfMemoryError error) {
                    error.printStackTrace();
                }
                if (values == null) {
                    Log.d(TAG, "onClick: values is null");
                } else {
                    Log.d(TAG, "onClick: values not null");
                }
                updateLeftMemoryView();
            }
        });
        mInButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    byte[] values = new byte[1024 * 1024 * 1024];
                } catch (OutOfMemoryError error) {
                    error.printStackTrace();
                }
                updateLeftMemoryView();
            }
        });
        mAddButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mArrayList.add(new byte[1024 * 1024 * 10]);
                } catch (OutOfMemoryError error) {
                    error.printStackTrace();
                }
                updateLeftMemoryView();
            }
        });
        mActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                byte[] bytes = new byte[1024 * 1024 * 10];
                updateLeftMemoryView();
            }
        });
        mGcButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                System.gc();
                updateLeftMemoryView();
            }
        });
        updateLeftMemoryView();

    }

    private void updateLeftMemoryView() {
        String s = String.format("內(nèi)存總計 : %#.3f , 剩余 : %#.3f", (Runtime.getRuntime().totalMemory() * 1.0 / 1024 / 1024), (Runtime.getRuntime().freeMemory() * 1.0 / 1024 / 1024));
        mLeftMemoryView.setText(s);
    }
}

測試界面

image

測試情況

變量在try外面

log如下

D: JIT code cache reset in 0 ms (0 bytes 1/0)
    GC_FOR_ALLOC freed 254K, 10% free 3058K/3376K, paused 10ms, total 12ms
I: Forcing collection of SoftReferences for 1073741836-byte allocation
D: GC_BEFORE_OOM freed 2K, 10% free 3056K/3376K, paused 9ms, total 12ms
E: Out of memory on a 1073741836-byte allocation.
I: "main" prio=5 tid=1 RUNNABLE
      | group="main" sCount=0 dsCount=0 obj=0x94c64bd8 self=0xb90c6500
      | sysTid=1571 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1216952576
      | state=R schedstat=( 0 0 0 ) utm=45 stm=23 core=0
        at com.yxf.trytocatchoutofmemory.MainActivity$1.onClick(MainActivity.java:~37)
        at android.view.View.performClick(View.java:4438)
        at android.view.View$PerformClick.run(View.java:18422)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5019)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
        at dalvik.system.NativeStart.main(Native Method)
W: java.lang.OutOfMemoryError
        at com.yxf.trytocatchoutofmemory.MainActivity$1.onClick(MainActivity.java:37)
        at android.view.View.performClick(View.java:4438)
        at android.view.View$PerformClick.run(View.java:18422)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5019)
        at java.lang.reflect.Method.invokeNative(Native Method)
W:     at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
        at dalvik.system.NativeStart.main(Native Method)
D: onClick: values is null

實際上這個OutOfMemoryError成功被catch到了,而且程序并沒有崩潰.

程序中的剩余內(nèi)存等也沒有發(fā)生太大變化,這說明OutOfMemoryError觸發(fā),其實并不會真正分配內(nèi)存.

而且從上面的日志信息上也可以了解到一些信息

比如:

  • 在分配可能會導(dǎo)致OutOfMemoryError的內(nèi)存前,會先做一次垃圾回收
  • 在做這次垃圾回收時會把SoftReference也回收掉
  • 變量并沒有被賦值成功

變量在Try里面

這個測試是為了測試是否會成功創(chuàng)建并且賦值的,由于上面結(jié)論已經(jīng)確定不行了,所以這個測試其實失去了意義.

添加數(shù)組測試

這個測試是模擬內(nèi)存泄漏的,類比反復(fù)的啟動一個被其他類引用的activity.

這里是一直給一個ArrayList添加數(shù)據(jù),直到接近OutOfMemory.

結(jié)果如圖

image

從圖中可知這個也可以catch到OutOfMemoryError

而且還可以了解到其他信息:

  • 應(yīng)用分配的總內(nèi)存并不是固定的,而是會根據(jù)使用情況增長的,而且這個總內(nèi)存是會根據(jù)手機ram改變的,模擬器2g內(nèi)存最大180mb,我真機6g內(nèi)存,最大380mb.

說個題外話,那么我們手機內(nèi)存明明都那么大了,卻依然還是卡的原因出來了!!!無良App就是,老子最重要,老子性能,體驗都要最好,然后無良App們就通過什么LruCache各種使用強引用緩存,占著內(nèi)存不放,然后它真的實現(xiàn)了,內(nèi)存都給它霸占著,系統(tǒng)是卡,其他后面開的App也卡,但是它就是不卡,23333333.

其他正常操作測試

這個測試是用來證明catch內(nèi)存泄漏導(dǎo)致的oom是否有意義的

測試結(jié)果如下

image

這個操作只是一個正常而又簡單的分配一個byte數(shù)組的操作,但是程序崩了.

由于之前的內(nèi)存泄漏已經(jīng)導(dǎo)致內(nèi)存幾乎使用完了,catch OutOfMemoryError雖然成功,但是并沒有意義,因為任何一個接下來的操作都可能依然會導(dǎo)致OutOfMemoryError的出現(xiàn).

總結(jié)

OutOfMemoryError不應(yīng)該去catch,出現(xiàn)OutOfMemoryError不管是因為一次巨大的內(nèi)存分配還是內(nèi)存泄漏導(dǎo)致,都是程序設(shè)計的問題,如果是大內(nèi)存操作,應(yīng)該想辦法一點點加載,或者壓縮資源來加載,如果是巨大數(shù)量的排序問題,則可以選擇外排序的方式進行,如果是內(nèi)存泄漏,則需要尋找程序自身的設(shè)計問題.

測試源碼

TryToCatchOutOfMemory

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弊琴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杖爽,更是在濱河造成了極大的恐慌敲董,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慰安,死亡現(xiàn)場離奇詭異腋寨,居然都是意外死亡,警方通過查閱死者的電腦和手機化焕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門萄窜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撒桨,你說我怎么就攤上這事查刻。” “怎么了凤类?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵穗泵,是天一觀的道長。 經(jīng)常有香客問我谜疤,道長佃延,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任茎截,我火速辦了婚禮苇侵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘企锌。我一直安慰自己榆浓,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布撕攒。 她就那樣靜靜地躺著陡鹃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抖坪。 梳的紋絲不亂的頭發(fā)上萍鲸,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音擦俐,去河邊找鬼脊阴。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘿期。 我是一名探鬼主播品擎,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼备徐!你這毒婦竟也來了萄传?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜜猾,失蹤者是張志新(化名)和其女友劉穎秀菱,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹭睡,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡衍菱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了棠笑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梦碗。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蓖救,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情印屁,我是刑警寧澤循捺,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站雄人,受9級特大地震影響从橘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜础钠,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一恰力、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旗吁,春花似錦踩萎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至码倦,卻和暖如春企孩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背袁稽。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工勿璃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓补疑,卻偏偏與公主長得像闻葵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子癣丧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容