【问题标题】:About memory leaks and generic methods关于内存泄漏和泛型方法
【发布时间】:2016-09-19 07:55:27
【问题描述】:

哪些更适合在我的应用中使用?

public class NetworkCheck {

    Context context;

    public NetworkCheck(Context context) {
        this.context=context;
    }

    public boolean isNetworkConnected() {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo() != null;
    }
}

...    

if(new NetworkCheck(this).isNetworkConnected()){
    //statement
}

对于上述方法,每当我必须使用它的方法时,我都必须创建堆内存。它的堆内存将在其作用域结束时被销毁(意味着花括号结束)......

或者。

public class NetworkCheck {

    public static boolean isNetworkConnected(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo() != null;
    }
}

...

if(NetworkCheck.isNetworkConnected(){
    //statement
}

为此,我不必创建任何堆内存。我读过很多文章,人们说创建静态变量和方法会导致应用程序内存泄漏。

请帮我创建下面方法的这个 Genric getLocalData() .....

 public static <T> void saveLocalData(Context context, String key, T value) {
        SharedPreferences prefs = context.getSharedPreferences(
                "Qikqrup", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        if (value instanceof String)
            editor.putString(key, (String) value);
        else if (value instanceof Boolean)
            editor.putBoolean(key, (Boolean) value);
        else if (value instanceof Integer)
            editor.putInt(key, (Integer) value);
        editor.commit();
    }

【问题讨论】:

  • 仅使用上面的代码,我看不到任何可能的内存泄漏。您可以同时使用两者。检查此blog.nimbledroid.com/2016/05/23/memory-leaks.html 以更好地了解内存泄漏。
  • 已经阅读了很多关于这方面的文章,你就是其中之一....thanx 的回复。
  • “我读过很多文章,人们说创建 STATIC 变量和方法会导致应用程序中的 MEMORY LEAK。”那不是真的。重新阅读文章。就像@Alan K. 所说,第二种方式更快。 15 - 20 % 根据文档
  • 请注意,T 变量中没有任何意义。只需将参数类型设为Object

标签: java android generics memory-leaks static


【解决方案1】:

第二种情况是不能被打败的,因为它是最紧的。

在第一种情况下,只要 NetworkCheck 对象存在,Context 对象就会一直存在。还有第二个对象(NetworkCheck)。

但是:

  • 它的使用受到限制,对象将迅速超出范围并被垃圾回收。因此,其效果与我们周围自发产生的粒子和反粒子一样显着。
  • 在其他情况下,构造函数和调用的分离,也许许多调用可能会被证明是有用的。

关于:

public static <T> void saveLocalData(Context context, String key, T value)

没用,Object 会更好。实际上 null 不会被捕获。一般来说,这个函数有一个部分域:不是所有的类都可以。再次检索该值将是猜测工作。

使用运行时信息可能会这样做。

public static <T> void saveLocalData(Context context, String key, Class<T> type, T value)
public static <T> T loadLocalData(Context context, String key, Class<T> type)

Java 有一个 Serializable 接口也可以使用。

public static <T> void saveLocalData(Context context, String key, Serializable value)

然后您会在存储时收到该对象。您需要阅读该主题。

【讨论】:

  • 先生,您能举个例子来解释一下吗:)
  • 我查看了 SharedPreferences,看来您的方法很标准:很多情况。关于Serializable:保存为二进制数据时效果最好。使用 SharedPreferences GSON 等可能是更好的选择。我无法举出一个完整的例子。
【解决方案2】:

在您的saveLocalData() 方法中,不一定存在内存泄漏,但可能存在内存泄漏,具体取决于您的使用方式。举个例子:

for( long i = 0;  i < Long.MAX_VALUE;  i++ )
{
    String name = String.valueOf( i );
    saveLocalData( context, name, i );
}

此循环将不断向您的地图添加值,直到内存不足。但是,任何集合都可能发生同样的情况,而不仅仅是地图,而且无论您的集合是否是静态分配的,都可能发生这种情况。使用静态收集比使用非静态收集更容易发生,因为可以想象非静态收集可能会超出范围并被垃圾收集,而静态收集通常注定要永远存在。

然而,这句话并不是绝对的:

  • 一方面,非静态集合很容易通过被静态对象引用而永久锚定到内存,而:

  • 另一方面,静态映射可能会被显式释放和重新分配,或者由不希望它变得太大的细心程序员简单地清除。


为了写getLocalData(),你需要声明如下:

public static <T> T getLocalData( Context context, String key, Class<T> classOfValue )

并按如下方式调用它:

String name = MyClass.getLocalData( context, "Name", String.class );

在函数中,您需要执行以下操作:

if( classOfValue == String.class )
    return editor.getString( key );
...

您的NetworkCheck 的两个版本以及您对每个版本的预期用途大致相同,在选择一个或另一个之间没有太大的收获或损失。执行new NetworkCheck(this) 代表了冗余内存分配,但正如您已经了解的那样,此内存很快就会被垃圾回收,因此不会造成损坏。

【讨论】:

    【解决方案3】:

    在这种情况下,我建议在这里使用静态方法版本,因为创建一个新对象只是为了检查连接性是多余的。

    “静态方法只是方法,它们不存储在堆中,它们只是无法使用“this”参数。”

    About memory leaks and generic methods

    【讨论】:

    • 您对这个问题的回答有什么新变化。你所说的已经被我提到了......感谢你的努力......
    • 静态变量可能会导致内存“泄漏”,仅当您打算在不使用它时将其设为空,并且在使用完后忘记清除它。就只有一个实例而言,这并不是真正的“泄漏”。
    【解决方案4】:

    STATIC 变量和方法导致 MEMORY LEAK

    这仅适用于类的静态字段,方法也不适用。这是真的,因为静态成员的值将在内存中存在,只要声明它们的类将驻留在内存中 - (或者,至少,只要您不将静态变量值设置为 null 允许GC 回收内存)。
    (顺便说一句:正确的术语是“存储的实例导致长期内存消耗”只有在这些实例从未使用过时才会成为泄漏,否则它们会成为“有用的长期内存使用”)。

    至于静态方法 - 如果它们不在静态变量中存储它们正在创建的任何实例,那么这些实例将:

    1. 如果本地变量/非返回实例 - 在超出范围后尽快进行 CG 编辑(注意:“尽快”不保证“立即” - 这是“尽可能”之后的意思全部)

    2. 如果它们是返回值 - 只有在调用者释放它们之后才会被 GC 处理(即不再“存储”它们以备将来使用)。关于返回值,您的静态方法就像一个“工厂”:忽略特定的配置顺序(假设比构造函数可以提供的更复杂),它们与仅使用 new ClassOfTheReturnedValue(...)

    关于public boolean isNetworkConnected()static public boolean isNetworkConnected():在您的使用示例中,它们之间没有区别在内存泄漏或创建将被GC-ed 的实例的能力方面 - 因为您的 NetworkCheck check=new NetworkCheck(this); 只是一个临时/局部变量,一旦超出范围就可以收集。
    这两种方法之间的唯一区别:非静态确实创建了一个对象(因此 CG down 的工作量更大轨道),而静态形式没有。


    关于public static &lt;T&gt; void saveLocalData(Context context, String key, T value)

    嗯,你发布它的方式非常不理想

    1. 如果使用除StringBooleanIntegerLongFloat 之外的任何内容调用该方法,编译器将根据请求连接调用。在执行时,您将花费不必要的类型检查(全部失败),只是调用editor.commit() 而没有真正的改变

    2. 关于类型安全,您拒绝编译器在编译时执行类型检查以进行所有运行时类型检查。您的方法与 public static void saveLocalData(Context context, String key, Object value) 没有什么不同 - 所有类型检查都推迟到运行时。泛型和“普通对象”形式之间的唯一区别是:编译器会更加努力地编译泛型方法,但会达到与“对象值”方法相同的最终结果。

    实现这一点的最佳方法是放弃泛型的所有智能并使用plain-old-method-overload,如下所示:

    protected static SharedPreferences.Editor resolveEditor(
      Context context, String prefOwner
    ) {
        SharedPreferences prefs = context.getSharedPreferences(
                prefOwner, Context.MODE_PRIVATE
        );
        return prefs.edit();
    }
    public static void saveLocalData(Context context, String key, boolean value) {
        SharedPreferences.Editor editor = resolveEditor(context, "sushildlh");
        editor.putBoolean(key, value);
        editor.commit();
    }
    public static void saveLocalData(Context context, String key, int value) {
        SharedPreferences.Editor editor = resolveEditor(context, "sushildlh");
        editor.putInt(key, value);
        editor.commit();
    }
    public static void saveLocalData(Context context, String key, String value) {
        SharedPreferences.Editor editor = resolveEditor(context, "sushildlh");
        editor.putString(key, value);
        editor.commit();
    }
    
    // etc
    

    以上内容:

    1. 编译器可以验证该方法没有被没有意义的类型值调用

    2. 代码是最优的

    毕竟,SharedPreferences.Editor 确实有理由不实现泛型的void putValue&lt;T&gt;(T value) - 是什么让您认为您可以在它之外做得更好?如果可以的话,我认为SharedPreferences.Editor 的作者会在他们的实现中做到这一点。

    【讨论】:

    • 感谢您的回答,但我正在寻找通用方法... :)
    【解决方案5】:

    saveLocalData() 方法不会导致任何内存泄漏,因为您不会保留任何超过其生命周期的引用。

    在使用活动上下文时,您应该注意以下几点:

    你不应该在课堂上使用活动上下文 活动的生命周期。

    例如:不要在数据库助手类或单例中使用活动上下文。

    关于 NetworkCheck,class2 更好,因为 isNetworkConnected() 方法是一个实用方法,它不代表 NetworkCheck 的任何状态。

    优点:

    1. 提高了性能,因为您没有实例化额外的对象。

    2. 不依赖于实例创建

    3. 可以在整个应用程序中轻松使用(在您执行网络操作的不同类中)
    4. 将使代码更易于理解

    缺点:

    1. 不能轻易模拟单元测试。

    【讨论】:

      【解决方案6】:
      public class AppStatus {
      
      private static AppStatus instance = new AppStatus();
      static Context context;
      ConnectivityManager connectivityManager;
      boolean connected = false;
      
      public static AppStatus getInstance(Context ctx) {
          if (ctx != null)
              context = ctx.getApplicationContext();
          return instance;
      }
      
      public boolean isOnline() {
          try {
              connectivityManager = (ConnectivityManager) context
                      .getSystemService(Context.CONNECTIVITY_SERVICE);
      
              NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
              connected = networkInfo != null && networkInfo.isAvailable() &&
                      networkInfo.isConnected();
              return connected;
      
      
          } catch (Exception e) {
              System.out.println("CheckConnectivity Exception: " + e.getMessage());
              Log.v("connectivity", e.toString());
          }
          return connected;
       }
      }
      

      要检查申请状态是否在线,请检查通过

      if ("package Name".AppStatus.getInstance(getActivity()).isOnline()) {}
      

      【讨论】:

      • 您可以通过edit解释它的工作原理来改进这个答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-06
      • 2015-04-03
      • 1970-01-01
      相关资源
      最近更新 更多