【问题标题】:Android caching events results in crashing if locking如果锁定,Android缓存事件会导致崩溃
【发布时间】:2015-09-16 10:32:31
【问题描述】:

当 Android 应用不在前台时,我将事件存储在队列中。事件可能很多,所以我想在达到一定限制时删除最旧的,以避免内存问题。事件可以是不同的类型,我想避免删除某些事件(如果不是真的需要,例如 onTrimMemory())。

public void enqueue(T event) {
    synchronized (events) {
        if (events.size() > 2000) {
            for (int i = 0; i < events.size(); i++) {
                if (canRemove(events.get(i))) {
                    events.remove(i);
                    break;
                }
            }
        }

        events.add(event);
    }
}

canRemove(event) 检查 Event 是否是可以删除的东西的 instanceof 并返回 true/false。

过了一会儿 Logcat 给了我:“long monitor contention event with owner method”,过了一会儿 Logcat 报告了

启动阻塞 GC Alloc

等待阻塞的 GC Alloc

将目标 GC 堆从 65MB 限制到 64MB,然后,在收到许多不同的消息后……应用程序崩溃了。

根据我在阅读类似问题 (What might be the cause of "long monitor contention event with owner method"?) 时的理解,问题是我收到了很多事件并在“太多”时间内锁定事件。

所以问题是……我能做什么?我可以通过保存我删除的最后一个位置并下次从中启动 for 循环来对其进行一些优化,但我认为这还不够。或者我可以为不同的事件做不同的队列,这样我总是删除第一个,但我总是需要锁定它。有更好的主意吗?

我忘了说我要声明:private LinkedList events = new LinkedList();

【问题讨论】:

    标签: java android garbage-collection queue concurrentmodification


    【解决方案1】:

    使用这个方法

       public void enqueue(T event) {
        List<T> removable = new ArrayList<>();
        synchronized (events) {
            final int size = events.size();
            if (size > 2000) {
                for (int i = 0; i < size; i++) {
                    final T t = events.get(i);
                    if (canRemove(t)) {
                        removable.add(t);
                        break;
                    }
                }
            }
            if (removable.size() > 0) {
                events.removeAll(removable);
            }
           events.add(event);
        }
    }
    

    希望它能解决您的问题。

    【讨论】:

    • 请注意,我在添加时一次删除一个事件,因此我丢失了最少数量的事件。
    • 谢谢。只是一个问题:考虑到事件是一个 LinkedList,在锁之外有 events.add() 不会导致 ConcurrentException?
    • 注意这个实现是不同步的。如果多个线程同时访问一个链表,并且至少有一个线程在结构上修改了链表,则必须对外同步。那么为什么 .add() 调用在外面呢?
    • 你可以同步整个方法,而不是一个块
    【解决方案2】:

    我认为您可以使用events.subList 方法来代替删除对象,如下所示:

    public void enqueue(T event) {
        List<T> removable = new ArrayList<>();
        synchronized (events) {
            final int size = events.size();
            if (size > 2000) {
                int removeSize = events.size() - 2000;
                events = events.subList(removeSize, events.size());
            }
           events.add(event);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-08-23
      • 2021-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多