Короче говоря, 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() для вызова функции JNI C-кода, передавая указатель, который освобождает память:
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);
То, что я думал, было правдой по поводу этого, так это то, что каждый пакет в списке передается второму классу по ссылке на объект. Если бы это было правдой, то независимо от того, что два класса делают со списком и объектами в них, мне гарантировано, что сборщик мусора будет вызывать finalize() только ОДИН РАЗ для каждого пакета (когда оба класса считаются завершенными с каждым пакетом). Это очень важно, иначе функция free() будет вызываться дважды для одного и того же указателя (что приведет к SEGFAULT).
Кажется, это происходит со мной. Как только второй класс получает ArrayList из широковещательной рассылки, он анализирует его и содержит несколько пакетов из списка. При этом есть другой объект/класс, который имеет член типа Packet, если мне "нравится" пакет, я сохраняю его, выполнив:
other_object.pkt = pktFromList;
В конце концов, этот метод, получивший широковещательную рассылку, возвращается, а некоторые пакеты сохраняются. Наконец, когда я нажимаю кнопку (через несколько секунд), 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
Таким образом, одно из двух предположений, которые я делаю относительно передаваемых объектов, неверно. Кто-нибудь знает, что это может быть, и как я могу более тщательно передавать объекты по ссылке, чтобы я мог правильно очистить память?
Если вы зашли так далеко в моем вопросе и поняли его, спасибо;)