почему мой Java-объект копируется или finalize () вызывается дважды? - PullRequest
2 голосов
/ 29 июля 2011

Длинная история Java / Android / JNI ... У меня есть два класса, объект / класс, который я создал, называемый Packet, и интерфейс JNI для низкоуровневого кода C, который я написал.В одном классе я анализирую входящие пакеты и сохраняю их в ArrayList.Когда они "анализируются", вызывается функция Packet.dissect (), которая использует JNI для вызова C-кода более низкого уровня.Этот C-код выполняет функцию malloc () и возвращает указатель памяти на код Java, который хранит его в закрытом элементе объекта Packet:

ArrayList<Packet> _scan_results;

    ...

        // Loop and read headers and packets
        while(true) {
            Packet rpkt = new Packet(WTAP_ENCAP_IEEE_802_11_WLAN_RADIOTAP);

            switch(_state) {

            case IDLE:
                break;

            case SCANNING:
                // rpkt.dissect() calls a JNI C-code function which allocates
                // memory (malloc) and returns the pointer, which is stored in
                // a private member of the Packet (rpkt._dissect_ptr).
                rpkt.dissect();
                if(rpkt.getField("wlan_mgt.fixed.beacon")!=null)
                    _scan_results.add(rpkt);

                break;
            }
        }

Теперь я хочу убедиться, что этопамять освобождается, чтобы у меня не было утечки.Поэтому, когда сборщик мусора определяет, что объект Packet больше не используется, я полагаюсь на finalize () для вызова функции C-кода JNI, передавая указатель, который освобождает память:

protected void finalize() throws Throwable {
    try {

        if(_dissection_ptr!=-1)
            dissectCleanup(_dissection_ptr);

    } finally {
        super.finalize();
    }
}

Отлично, это прекрасно работает ДО Я пытаюсь поделиться ArrayList со вторым классом.Для этого я использую Broadcast в Android, отправляя трансляцию со списком ArrayList из первого класса в BroadcastReceiver во втором классе.Вот где оно транслируется из первого класса:

    // Now, send out a broadcast with the results
    Intent i = new Intent();
    i.setAction(WIFI_SCAN_RESULT);
    i.putExtra("packets", _scan_results);
    coexisyst.sendBroadcast(i);

То, что я думал , было правдой в отношении этого, будет то, что каждый пакет в списке передается во второй классссылка на объект.Если это так, то независимо от того, что эти два класса делают с List и объектами в них, я гарантирую, что сборщик мусора будет вызывать только finalize () ONCE для каждого пакета (когда оба классасчитается законченным с каждым пакетом).Это очень важно, иначе free () будет вызываться дважды по одному и тому же указателю (вызывая SEGFAULT).

Кажется, это происходит со мной.Как только второй класс получает ArrayList из широковещательной рассылки, он анализирует его и содержит несколько пакетов из списка.При этом существует другой объект / класс, который имеет член типа Packet. Если мне «нравится» пакет, я сохраняю его, выполняя:

other_object.pkt = pktFromList;

В конце концов этот метод, получивший Broadcast, возвращаети некоторые из Пакетов были "сохранены".Наконец, когда я нажимаю кнопку (несколько секунд спустя), ArrayList в исходном классе очищается:

_scan_results.clear();

Я предполагал, что даже когда здесь вызывается clear (), если первый класс имеет "Сохраняя "некоторые пакеты, сборщик мусора не будет вызывать finalize () для них.Единственным способом для этого будет ложь, если: (1) при отправке широковещания ArrayList копируется и не передается по ссылке, или (2) когда пакеты «хранятся» во втором классе, объекте Packetскорее копируется, чем хранится по ссылке.

Что ж, один из них ложный, потому что free () иногда вызывается в одном и том же фрагменте памяти дважды.Я вижу, что он выделен («новое вскрытие: MEMORY_ADDRESS1 - IGNORE_THIS»), а затем вызывается два finalize (), пытающихся освободить один и тот же адрес памяти:

INFO/Driver(6036): new dissection: 14825864 - 14825912
...
INFO/Driver(6036): received pointer to deallocate: 14825864
...
INFO/Driver(6036): received pointer to deallocate: 14825864
INFO/DEBUG(5946): signal 11 (SIGSEGV), fault addr 0000001c

Итак, одно из двух предположений, которые яЯ делаю об объектах, которые передаются вокруг ложно.Кто-нибудь знает, что это может быть, и как я могу передать объекты более тщательно по ссылке, чтобы я мог очистить память должным образом?

Если вы сделали это так далеко через мой вопрос и поняли это, спасибо;)

1 Ответ

2 голосов
/ 29 июля 2011

Дополнительные функции передаются по значению, а не по ссылке. Это означает, что копия того, что передается, создается получателем при получении намерения.

Предположительно, вы пометили свой класс Packet как Parcelable - чтобы заставить его работать как дополнительное намерение. Посылки являются маршалированными и не маршалируемыми - вы должны изменить свой код маршалинга для Packet, чтобы отбрасывать указатель памяти, чтобы он не передавался копии, чтобы он не мог быть выпущен дважды.

(Расширение, основываясь на комментариях ниже и требовании, чтобы оно действовало «как если бы» было передано по ссылке)

Создать глобальный HashMap<int,ArrayList<Packet>> passed_packets;

При создании намерения добавьте объект, который сейчас передается в качестве дополнительного, в HashMap:

synchronized(passed_packets) {
    passed_packets.put(_scan_results.hashCode(), _scan_results);
}

Добавить хэш-ключ к цели вместо фактического объекта

i.addExtra("packets_key", _scan_results.hashCode())

Получая намерение, получите хеш-ключ, получите значение из HashMap и удалите его из HashMap

synchronized(passed_packets) {
    int key = i.getInt("packets_key");
    scan_results = passed_packets.get(key);
    passed_packets.remove(key);
}
...