zmiany rozmiaru dostępnej pamięci

0

W pisanym programiku chciałbym, aby jego możliwości zależały (ograniczały się) od ilości pamięci RAM komputera, na którym jest uruchamiany. (aplikacja jest w postaci pliku .jar)
Czy jest to w ogóle możliwe i jak to wykonać?

Mniej więcej widziałbym to tak:

  1. program uruchamia obszar roboczy- z takimi ustawieniami ze u kazdego sie odpali.
  2. jesli wybierze sie plik-->nowy to wyskakuje okno gdzie mozna podac ustawienia nowego projektu.
    No i wlasnie tutaj chcialbym sprawdzac ilosc wolnej pamieci RAM (nie tylko wartosci maksymalnej przydzielonej JVM) i na tej podstawie mowic użytkownikowi:" Ok-zgadzam sie na taki projekt"; lub: "Nie ma szans-za malo pamieci".

W przypadku zgody nastepuje zwiekszenie pamieci przydzielonej JVM.

Czy takie dynamiczne zmiany z poziomu aplikacji pamieci JVM sa w ogole mozliwe (jak to wykonac?)?
Jesli nie, to czy pozostaje jedynie przydzielenie aplikacji skonczonego zasobu pamieci w chwili jej uruchomienia? Czy jest moze jeszcze jakies inne rozwiazanie na ktore nie wpadlem.

0

Podejście 1. W projekcie ustawiasz maksymalną możliwą wartość rozmiaru pamięci (-xmx1500m). Java i tak przydzieli sobie na starcie domyślną minimalną wielkość, a potem tylko tyle ile będzie w stanie (zwykle jest to do 1,5 GB RAM na wersji 32-bit) ponieważ potrzebuje przydziału największego spójnego obszaru. Wtedy program javowy będzie miał do dyspozycji tyle pamięci w VM ile faktycznie będzie możliwe.
Podejście 2. Robisz projekt z najmniejszymi możliwymi ustawieniami, które będą w stanie załadować najmniejsze gui. Wielkość dostępnej pamięci sprawdzisz przez jni i na tej podstawie odpalisz (lub nie) inną aplikację z maksymalnymi realnie możliwymi ustawieniami pamięci za pomocą wywołania java.exe.

W praktyce to pierwsze podejście jest prostsze i szybsze bo java nie bierze od początku tyle ile może, chyba że to wymusisz przez -xms. Jeżeli jednak aplikacja weźmie całą maksymalną pamięć, to pamięć VM rozrośnie się do prawie 2GB i już nie zwolni aż do zamknięcia JVM. Zwolnienia pamięci przez aplikację nic już nie dadzą ponieważ JVM nie potrafi oddawać pobranej od systemu pamięci. Oddaje tylko raz i to całą - kiedy jest zamykana. Krótko mówiąc - minimalny xms, maksymalny xmx, a aplikacja nie przydziela sobie pamięci jeżeli naprawdę nie potrzebuje.

0

No dobra

  • spakowałem aplikację do jara,
  • zrobiłem plik startowy z poleceniem: java -Xms512m -Xmx512m -jar MojaNazwa.jar.

W jaki sposób teraz określać czy żądana liczba tworzonych obiektów nie doprowadzi do: " java.lang.OutOfMemoryError: Java heap space " ??
No bo chyba robienie tego w ten sposób:

 
        (..)
	Runtime rt = Runtime.getRuntime();
        (...)
        System.out.println("Free memory: "+rt.freeMemory());

Nie prowadzi do niczego dobrego, gdyż metoda freeMemory() nie podaje ilości wolnej pamięci do granicy określonej parametrem -Xmx512m, a jedynie do końca aktualnie używanego jej rozmiaru ( ustawienie na starcie Xms=Xmx nie pomaga, gdyż i tak Xms jest mniejszy od Xmx). W trakcie tworzenia kolejnych elementów, gdy "dostępna pamięć" zbliża się do wyczerpania uruchamiany jest GarbageCollection i metoda freeMemory() ponownie wykazuje większą liczbę zasobów.
No i tak w kółko... aż do zawalenia całego heap'a.
Więc taki sposób nie zdaje egzaminu.

A może można od drugiej strony, tj. zamiast sprawdzać ilość pamięci do końca określać ilość pamięci już zajętej ??

Ktoś ma jakiś pomysł na to?

0

Policzenie ile byłoby wolnego miejsca po odśmieceniu wg mnie trwałoby niewiele krócej (np 2x krócej) niż sam właściwy proces odśmiecania, a więc byłaby to kosztowna operacja. Musisz więc zasugerować JVM odpalenie GC, poczekać na zakończenie odśmiecania i wtedy pobrać wielkość pamięci. W okienku dialogowym, mówiącym o braku pamięci możesz dodać przycisk sugerujący JVM odpalenie GC oraz mierzyć co chwilę ilość raportowanej wolnej pamięci. Jeśliby ilość wolnej pamięci zwiększyła się ponad wymagany poziom to okienko znika i odpala się co się ma odpalać.

0

Wibovit:
Jak już mówiłem ustawienie -Xms na 512m, nie jest wykazywane przez metodę freeMemory(). Podaje ona jedynie jakaś wartość dostępna zanim nastąpi odpalenie GC.
Gdyby zrobić tak jak sugerujesz to:

  1. Rozumiem z tego co mówisz że kolejne wywołania GC mają dążyć w kierunku umożliwienia mi wartości podanej w parametrze -Xms ?
  2. Jeśli nawet w pętli sprawdzałbym czy na skutek kolejnych wywołań GC jest już ileś tam pamięci, to by wiedzieć czy ta ilość jest wystarczająca musiałbym przeliczyć ile pamięci potrzeba do utworzenia liczby obiektów, której potrzebuje.
    Z tego co czytałem do tej pory to do wyznaczenia pamięci potrzebnej dla tablicy obiektów nie wystarczy znajomość ilości pamięci potrzebnej na jeden obiekt i pomnożenie tego razy ich ilość. Tym bardziej jeżeli jest to tablica wielowymiarowa (dochodzą tam jeszcze jakieś wskaźniki....). Np tablica obiektów obiekty[a][b][c] wymaga innej wielkości zasobów niż tablica obiekty[c][b][a] (założenia a,b,c, to inne liczby).
    Jaki jest na to przelicznik?
0

Java ma nieco "zmanipulowane" wielkości tego co nazywa się pamięcią.
Sprawa wygląda tak:

  1. Cała zadeklarowana pamięć możliwa do przydzielenia dla bieżącego procesu JVM to wynik metody: Runtime.maxMemory(). Jest to odpowiednik odczytania wartości opcji -xmx.
  2. Cała pamięć aktualnie przydzielona od systemu dla bieżącego procesu JVM to wynik metody: Runtime.totalMemory()
  3. Cała już użyta pamięć, to wynik działania: Runtime.totalMemory() - Runtime.freeMemory()
  4. A cała wolna pamięć możliwa do użycia to wynik działania:
    Runtime.freeMemory() + Runtime.maxMemory() - Runtime.totalMemory()
    W tym ostatnim przypadku jeżeli po sprawdzeniu tej wielkości jakieś inne procesy (nie Javowe) zabiorą od systemu pamięć, to JVM może nie otrzymać od systemu dodatkowej pamięci (do granicy Runtime.maxMemory()) bo pamięć procesów "niejavowych" + pamięć wszystkich procesów JVM musi być nie większa niż cała pamięć wirtualna systemu. Stan taki może się zdarzyć w systemach 32-bitowych z maks. 2 GB RAM bez ustawionego swapfile.

Nie ma kompletnie znaczenia czy bawimy się gc czy nie, a skutek jego działań nie zmieni wartości wolnej pamięci liczonej w punkcie 4.

Runtime.freeMemory() oznacza tak naprawdę ilość pamięci "totalnie wolnej", czyli takiej której zajęcie nie spowoduje ani działania odśmiecarki, ani wywołania systemowego (czyli nie spowoduje niekontrolowanego narzutu użycia CPU).
Tak dokładna informacja o wolnej pamięci jest potrzebna niezwykle małej grupie programów javowych - np. tym "sławnym" aplikacjom Javowym do kontroli ruchu lotniczego ;).

Jeszcze co do użycia pamięci. Każdy najmniejszy obiekt (w tym obiekt dowolnej tablicy zeroelementowej) zajmuje 16 bajtów, każde pole obiektowe (czyli każda referencja 32-bitowa) zajmuje 4 bajty, a pole proste tyle ile jest określone w definicji języka (np. long, to 64-bity czyli 8 bajtów).
Tak więc pamięć przydzielaną na każdy obiekt można policzyć na sucho (co jest założeniem Javy). Nie jestem jednak pewien jak wygląda sprawa z liczeniem zajętości pakowanych referencji 64-bitowych bo nie przeryłem jeszcze całej specyfikacji Java 7.

0

OutOfMemoryError można od biedy łapać. Zakładając, że przerwanie jakiejś funkcji alokującej obiekt spowoduje powstanie wielu niedostępnych obiektów (czyli śmieci, w terminologii platform zarządzanych), to po wystąpieniu tego błędu GC powinno te obiekty zebrać i zwolnić pamięć.

NetBeans Platform, dla przykładu, łapie błędy typu OutOfMemoryError, a potem resetuje moduł, który ten błąd spowodował. Raz miałem coś takiego, że parser wpadał w jakąś okropną rekurencję i zapychał całą pamięć. NetBeans w takim przypadku po prostu wyłączał parser na danym kodzie źródłowym.

0

No więc tak... (dużo tego się nazbierało):
Zacząłem od ustalenia ile zajmują obiekty, które tworze. Skorzystałem do tego z małego programiku znalezionego tutaj:
http://www.roseindia.net/javatutorials/determining_memory_usage_in_java.shtml

No i wynik jest 472 bajty na obiekt mojej klasy "Panelik". W innej klasie (klasie głównej programu) tworze obiekty tej klasy dodajac do nich listenera (fragmet kodu):

 
      pole[k][i][j] = new Panelik();
      obsluga obsl = new obsluga(k,i,j);
      pole[k][i][j].addMouseListener(obsl);

Ile zajmuje klasa obsluga? Wychodzi na to ze 24bajty. Sprawdzone rownież tym samym programikiem. No i dla pewności sprawdziłem jeszcze, ile zajmuje obiekt klasy Panelik wyposazony w takiego listenera, no i jest 496 bajtow. (Pomijam milczeniem to jak bardzo mało efektywne jest: a) tworzenie tablicy 3 wymiarowej tak duzych obiektow, b) dodawanie tego MouseListenera . Niestety wcześniej nie zdawałem sobie z tego sprawy, no ale zostawmy to, bo akurat tutaj wiem co i jak poprawić. Tak czy siak widze inny problem w calym tym dzialaniu.
Mianowicie:
Podazylem za sugestia i podpowiedziami Olamagato, tj: ustawilem identyczne wartosci dla Xms i Xmx oraz wykorzystuje metody Runtime'a w przedstawiony wczesniej sposob. Czyli. uruchamiam cala aplikacje z jakimis podstawoymi ustawieniami. Na koniec sprawdzam ile to wszystko zajelo pamieci. Aplikacje mozna podzielc na czesc "o niezmiennej wielkosci" (stalej bez wzgledu na wielkosc realizowanego projektu) oraz "czesc zmienna". Do obliczenia ilosci zajmowanej pamieci przez "czesc zmienna" stosuje napisana metode (przelicznik na podstawie opisu wspomnianego przez Olamagato. Zakladam ze moj przelicznik jest prawidlowy, gdyz przedstawia wartosci zgodne z programikiem znalezionym w necie)
No i teraz w momencie gdy uzytkownik decyduje ze program oferuje mu zbyt male mozliwosci pobieram wartosci ktorych on żada. I dokonuje sprawdzenia warunku:
("waga" calej aplikacji) - ("waga" jej "części zmiennej")+("waga" żądanej części zmiennej (z przelicznika)) < mojRuntime.maxMemory() ;

No i do tego co dłużej nie jest potrzebne przypisuje null'e i tworze nowe, odpowiednio wieksze poszczegolne skladowe.

Do obserwacji tego co dzieje się z pamięcią używam JConsole i zastanawia mnie kilka rzeczy:

  1. Czy wskazane jest takie maksymalne zapychanie pamięci,czy może jakiś procent dobrze byłoby pozostawić wolny (jaki?) ?

  2. Czy dobrze by bylo ustawic JVM jakies jeszcze parametry poza -Xmx i Xms dotyczace podzialu poszczegolnych czesci heap'a (jakie?) ?.

  3. Zastanawia mnie rowniez sam GC. No bo chyba mozna go tez jakos dobrac, wydaje mi sie ze nie ma on zbyt duzo roboty poza momentami wywolywanych przeze mnie przeladowan (myslalem nad "Concurrent Low Pause Collector" aby posprzatac raz a dobrze zamiast angazowac GC czesto bez sensu.

(To moje pierwsze zetkniecie z obsluga Jconsole i tematem GC, więc prosze o wyrozumialość. )

aa... i jeszcze jedno, zauważylem ze metoda maxMemory() zwraca rozne wartosci nawet w niewielkich odstepach czasu (rowniez w jconsole zauwazam przebieg piłokształtny zajętej pamięci), co chyba moze powodowac katastrofe jesli dokonuje sprawdzenia warunku przedstawionego wyzej?

1 użytkowników online, w tym zalogowanych: 0, gości: 1