2015-05-12 33 views
10

Próbuję pobrać 784 MB pamięci. Tak, wiem, że to dużo jak na telefon 32-bitowym, ale następujące wezwanie pracował przed Android 5.0:Wywołanie połączenia Android NDK mmap na urządzeniach 32-bitowych po aktualizacji do Lollipop

mmap(0, 0x31000000, PROT_NONE, MAP_ANON | MAP_SHARED, -1, 0); 

Jednak na trzech różnych urządzeń pochodzących od różnych producentów, podnoszących na Androidzie 5.0 złamał ten. Zakładam, że jest to jakaś zmiana w alokacji pamięci w wersji 5.0; może trzeba przekazać inne flagi?

Oto komunikat błędu zwrócony w LogCat:

E/libc﹕ mmap fail (pid 9994, tid 10125, size 822083584, flags 0x21, errno 12(Out of memory)) 
+0

Komunikat o błędzie wydaje się dość oczywisty; czy sprawdziłeś, czy dostępna jest wystarczająca ilość pamięci? – Phillip

+0

Nie wiem na temat solidnego sposobu sprawdzenia, ile pamięci jest wolne i dostępne przez kod natywny. Jednak to nie jest naprawdę ta część, której nie rozumiem - nie rozumiem, dlaczego ten sam kod działa na tym samym urządzeniu z Androidem 4.4, a następnie nie działa w wersji 5.0. Jest mało prawdopodobne, że ślad pamięci Androida zwiększył się tak bardzo - wydaje mi się, że jest bardziej prawdopodobne, że wymagana jest jakaś nowa flaga kompilatora lub coś w tym stylu. – sigmabeta

+0

Po prostu użyj 'adb shell free -m' z podłączonego komputera. Jeśli nie masz dostępu do urządzenia, SO ma różne rozwiązania, aby zapytać o dostępną pamięć z C w systemie Linux. (Powinieneś * naprawdę * to zrobić.) – Phillip

Odpowiedz

4

W miejscu, gdzie mmap() zawiedzie, otwartej /proc/self/maps i skopiować zawartość do pliku tymczasowego, a następnie zbadać plik w edytorze. Powinieneś zobaczyć kilka takich wpisów:

12e01000-42c00000 ---p 00201000 00:04 11639  /dev/ashmem/dalvik-main space (deleted) 
55281000-5d500000 r--s 00000000 00:16 61   /storage/sdcard1/blah 
5d500000-67e80000 rw-p 00000000 00:00 0   [anon:libc_malloc] 
67ea4000-682cc000 r-xp 00000000 b3:17 114807  /system/vendor/lib/libsc-a3xx.so 
682cc000-682f4000 r--p 00427000 b3:17 114807  /system/vendor/lib/libsc-a3xx.so 

Liczby po lewej stronie to wirtualne zakresy adresów (początek/koniec) dla procesu. Kiedy tworzysz nowe mapowanie, musi ono zmieścić się w luce między mapowaniami.

W powyższym przykładzie istnieje spora, duża luka między końcem pierwszego wpisu 0x42c00000 a początkiem następnego wpisu 0x55281000. To około 294 MB. Między następnymi dwoma nie ma miejsca, a po nim tylko mały.

Jeśli spojrzysz na swoją mapę procesu i nie znajdziesz luki wystarczająco dużej, aby pomieścić plik, masz odpowiedź. Region pomiędzy 0x00000000 i 0xbfffffff jest ogólnie dostępny dla aplikacji 32-bitowych, ale struktura aplikacji wykorzystuje go bardzo dużo. (Pierwszy 1 GB jest zmapowany do jądra.)

Domyślam się, że niektóre kombinacje ASLR i zmiany sposobu przydzielania pamięci wirtualnej w Lollipop doprowadziły do ​​tego problemu. Na mapie dołączonej do this similar question największą znalezioną luką było około 300 MB. Istnieją dwa duże regiony "dalvik", jeden 768MB (przy 12e01000), jeden 1,2GB (przy 84d81000). (Ponieważ używasz Lollipopa, są one raczej wynikiem ART, a nie Dalvik, ale najwyraźniej etykieta utknęła.)

Jedną z możliwości jest to, że ART ma wyższe wymagania pamięci wirtualnej niż Dalvik, a duże przydziały utrudniają aplikacjom uzyskanie dużych mapowanych regionów. Możliwe jest również, że ART ma zbyt dużo alokacji z powodu błędu. Możesz przetestować na Marshmallow, aby sprawdzić, czy coś jest naprawione.

Niezależnie od przypadku nie można utworzyć odwzorowania, jeśli nie ma ciągłego obszaru adresu pamięci wirtualnej wystarczająco dużego, aby go pomieścić. Przy użyciu frameworka aplikacji i olbrzymich alokacji ART widocznych w drugim pytaniu mapowanie 768MB nie byłoby możliwe, nawet gdyby wirtualna przestrzeń adresowa nie była pofragmentowana. Będziesz musiał zmapować mniejsze fragmenty pliku i ewentualnie je odwzorować podczas pracy, aby zrobić miejsce.

Możliwe, że warto zgłosić błąd na b.android.com. Załącz kopię pliku mapy procesu i określ wersję Androida i urządzenia.

Aby uzyskać więcej informacji na temat interpretowania danych wyjściowych/proc/maps, zobacz np. this answer.

+0

To jest doskonała odpowiedź. Minie trochę czasu zanim zacznę eksperymentować z jakimkolwiek rozwiązaniem tego problemu (a w międzyczasie projekt jest ograniczony tylko do 64-bitowych urządzeń), ale brzmi to tak, jakby odpowiadał przynajmniej na pytanie dlaczego aktualizacja systemu operacyjnego zepsuła dany kod. – sigmabeta

0
  • Recompile bibliotekę aplikacji, aby dopasować lizaka i spróbuj ponownie
  • Zaktualizuj Twój SDK
  • Koniecznie ustawić platformę docelową lizak na swoje ustawienia aplikacji
  • Zmniejsz przydzieloną pamięć i sprawdź ponownie
+0

Doceniam SDK 21 zarówno w manifeście, jak i NDK. Ilość potrzebnej pamięci nie podlega negocjacji. – sigmabeta

0

Czy wypróbowałeś opcję largeHeap?

W wyjątkowych sytuacjach można zamówić większy rozmiar sterty przez ustawienie atrybutu largeHeap na „true” w oczywisty tagu. Jeśli to zrobisz, możesz wywołać metodę getLargeMemoryClass() pod numer , aby uzyskać oszacowanie dużego rozmiaru sterty.

https://developer.android.com/training/articles/memory.html#CheckHowMuchMemory

Szczegóły:

https://developer.android.com/guide/topics/manifest/application-element.html#largeHeap

+2

Rozumiem, że ma to wpływ na zarządzaną stertę pamięci, a nie na kod natywny. Czy się mylę? – sigmabeta

+0

Jak rozumiem, tak. – mayo