JAVA АБ'ЕКТЫ І СПАСЫЛКІ

У дадзеным артыкуле разглядаецца выкарыстанне класаў з стандартнага пакета 
java.lang.ref
, такіх як 
SoftReference
WeakReference
PhantomReference
.

Разгледзім прыклад калі гэтыя класы могуць спатрэбіцца. 

Ёсць такая задача: неабходна рэалізаваць загрузку аб'ектаў з сервернай боку на кліенцкую бок па патрабаванні кліента.
Для гэтага можна рэалізаваць калекцыю аб'ектаў на кліенце, у якой будуць захоўвацца ўжо загружаныя аб'екты, для таго каб пры
паўторным запыце гэтага ж аб'екта не даводзілася загружаць яго зноў, а аддаваць іх з гэтай калекцыі, г.зн. рэалізацыя кэша на кліенце.
З цягам часу кліент загрузіць шмат новых аб'ектаў, захавае іх у калекцыю, а раней загружаныя аб'екты становяцца ўжо
ня актуальнымі і проста займаюць месца ў памяці. Разам - неабходны механізм ачысткі кэша.

Тут можна пайсці рознымі шляхамі, разгледзім некаторыя:

1. можна сачыць за тым, калі аб'ект перастае выкарыстоўвацца на кліенце і затым выдаляць яго з калекцыі.
Аднак, гэтае рашэнне патрабуе шматлікіх змен у логіцы працы кліента, і будзе ідэальна каб іншыя класы працавалі з гэтымі аб'ектамі празрыста. 

2. Выкарыстоўваць механізм «слабых спасылак» ў Java.

Што гэта за «слабыя спасылкі»?

У Java спасылкі можна падзяліць па «сіле».
Далей прадстаўлены ўсе 4 тыпу спасылак адсартаваных па «сілу" (ад большай да меншай):

1.  моцныя спасылкі (strong reference) . Гэта звычайныя спасылкі якія мы заўсёды выкарыстоўваем. Калі мы аб'яўляем Rectangle rect = new Rectangle (), то аб'ект
на які спасылаецца rect не можа быць выдалены зборшчыкам смецця з памяці да таго часу, пакуль на гэтае месца ёсць хоць бы адна моцная спасылка;

2.  мяккія спасылкі (SoftReference) . Аб'яву мяккай спасылкі выглядае так:

SoftReference <прастакутнік> Rect = новы SoftReference <прастакутнік> (новы прастакутнік ());

Для атрымання самога аб'екта Rectangle можна скарыстацца метадам rect.get (). Метад get () абвешчаны ў класа Reference, ад якога ўспадкоўваюцца
SoftReference, WeakReference, PhantomReference. Важна разумець, што метад get () можа вярнуць null. Гэта адбываецца ў тым выпадку, калі
памяць вашай праграмы запоўненая і з'яўляецца верагоднасць узнікнення OutOfMemoryError, тады зборшчык смецця выдаляе аб'екты на якія спасылаюцца
мяккія спасылкі. Менавіта гэты від спасылак зручна выкарыстоўваць для кэшавання аб'ектаў.

3.  слабыя спасылкі (WeakReference) .
Выкарыстанне слабой спасылкі падобна з выкарыстаннем мяккай спасылкі, аб'ява:

WeakReference <прастакутнік> Rect = новы WeakReference <прастакутнік> (новы прастакутнік ());

Розніца ў тым, што аб'ект на які спасылаецца слабая спасылка можа быць выдалены ў любы момант пры выкліку cборщика смецця, г.зн.
для гэтага не абавязкова ўмова недахопу памяці.

4.  фантомныя спасылкі (PhantomReference) .
Стварэнне фантомнай спасылкі аналагічна іншым:

PhantomReference Прастакутнік = 

новы PhantomReference (новы прастакутнік (), чарга);

Розніца ў тым, што тут абавязкова аб'яву аб'екта
чарзе queue - гэта прадстаўнік класа ReferenceQueue. Чарзе выкарыстоўваюцца таксама з SoftReference і WeakReference. Іх функцыя ў тым,
што калі аб'ект даступны з дапамогай спасылкі выдаляецца, то сам аб'ект спасылкі становіцца даступным у чарзе ReferenceQueue. Гэты
механізм дазваляе выдаляць пустыя спасылкі.

Метад get () выкліканы ў фантомнай спасылкі заўсёды вяртае null. Гэта
звязана з тым для чаго яны выкарыстоўваюцца.

Чытай яшчэ:   выраўноўванне ў вочка табліцы HTML ПА ЦЭНТРУ

Справа ў тым, што слабая спасылка змяшчаецца ў чаргу перад тым, як аб'ект на які яна паказвае будзе
фіналізаваным (finalize ()) і выдалены зборшчыкам смецця. Г.зн. ў метадзе finalize () выдалянага аб'екта можна прызначыць строгую
спасылку на выдаляны аб'ект, і такім чынам «выратаваць» яго ад зборшчыка смецця. Але слабая спасылка ўжо будзе ў чарзе і будзе знішчана.

Адрозненне фантомнай спасылкі ў тым, што яна змяшчаецца ў чаргу толькі «па факце» выдалення аб'екта на які яна паказвае - гэта і ёсць яе асноўная функцыя.

Разгледзім слабыя спасылкі на практычным прыкладзе.

Аб'екты на якія будуць указваць нашы спасылкі прадстаўленыя класам BigObject.

class BigObject {
  private int value;
  public BigObject(Integer pValue) {
    value = pValue;
  }
  @Override
  protected void finalize() throws Throwable {
    System.out.println("BigObject ("+value+") finalize()");
  }
  @Override
  public String toString() { return value+"";  }
}

Для тэставання выкарыстоўваюцца наступныя аб'екты:

  List strongList; // сильные ссылки
        List> softList; // мягкие ссылки
        List> weakList; // слабые ссылки
        List> phantomList; // фантомные ссылки
        ReferenceQueue queue; // очередь
        List loadMemoryList; // здесь будут храниться ссылки на строки, которые
        // используются для загрузки памяти

Для высновы вынікаў тэстаў будзем выкарыстоўваць наступныя метады:

private void printLists(){
  System.out.println("Strong references: ");
  for (BigObject bo : strongList) System.out.print(bo+" ");
  System.out.println();
  System.out.println("SoftReferences: ");
  printList(softList);
  System.out.println("WeakReferences: ");
  printList(weakList);    
  System.out.println("PhantomReferences: ");
  printList(phantomList);
}

private void printList(List> pList){
  for (Reference ref : pList)
    System.out.print(ref.get()+" ");

          System.out.println();
}

Метады ініцыялізацыі і загрузкі памяці выглядаюць так:

private void init(){
  strongList = new ArrayList();
  softList = new ArrayList>();
  weakList = new ArrayList>();
  phantomList = new ArrayList>();
  loadMemoryList = new ArrayList();
  queue = new ReferenceQueue();
  for (int i=0; i<3; i++){
    strongList.add(new BigObject(i));
    softList.add(new SoftReference(new BigObject(i)));

            weakList.add(new WeakReference(new BigObject(i)));
    phantomList.add(new PhantomReference(new BigObject(i), queue));
  }    
  printLists();
}

private void loadMemory(){
  for (int i=0; i<1200000; i++){
    loadMemoryList.add(i+"");
  }
}

Цяпер правядзем тэставанне. Для першага тэсту выкарыстоўваем наступны код:

public void testPhantomReferences(){
  init(); // инициализация
  System.gc(); // вызов сборщика мусора
  System.out.println("garbage collector invoked");
  printLists(); // вывод
}

У выніку атрымліваем:


        Strong references:
        0 1 2
        SoftReferences:
        0 1 2
        WeakReferences:
        0 1 2
        PhantomReferences:
        null null null
        garbage collector invoked
        Strong references:
        0 BigObject (2) finalize()
        BigObject (2) finalize()
        BigObject (1) finalize()
        BigObject (1) finalize()
        BigObject (0) finalize()
        BigObject (0) finalize()
        1 2
        SoftReferences:
        0 1 2
        WeakReferences:
        null null null
        PhantomReferences:
        null null null
    

Тлумачэнне: Як раней гаварылася, фантомныя спасылкі заўсёды вяртаюць null.
Пасля выкліку зборшчыка смецця былі выдаленыя аб'екты на якія спасылаліся фантомныя спасылкі (PhantomReference) і слабыя спасылкі (WeakReference).
У другім цесцю разгледзім момант выдалення аб'ектаў на якія спасылаюцца мяккія спасылкі (SoftReference). Тэст прадстаўлены кодам:


public void testSoftRefences(){
  init();
  System.gc();
  System.out.println("garbage collector invoked");
  printLists();
  System.out.println("memory usage increased");
  loadMemory(); // загрузка памяти
  System.out.println("loadMemoryList.size() = "+loadMemoryList.size());
  System.gc();
  System.out.println("garbage collector invoked");
  printLists();    
}

У выніку атрымліваем:


        Strong references:
        0 1 2
        SoftReferences:
        0 1 2
        WeakReferences:
        0 1 2
        garbage collector invoked
        Strong references:
        BigObject (2) finalize()
        BigObject (1) finalize()
        BigObject (0) finalize()
        0 1 2
        SoftReferences:
        0 1 2
        WeakReferences:
        null null null
        memory usage increased
        loadMemoryList.size() = 1200000
        garbage collector invoked
        BigObject (2) finalize()
        BigObject (1) finalize()
        BigObject (0) finalize()
        Strong references:
        0 1 2
        SoftReferences:
        null null null
        WeakReferences:
        null null null
    

Тлумачэнне: На першым этапе пасля выкліку зборшчыка смецця былі выдаленыя аб'екты на якія ўказвалі слабыя спасылкі (WeakReference).
Далей адбываецца загрузка памяці і паўторны выклік зборшчыка смецця. Пасля гэтага выдаляюцца аб'екты на якія
ўказвалі мяккія спасылкі (SoftReference). У выніку пасля гэтых дзеянняў засталіся аб'екты толькі з прамымі (моцнымі) спасылкамі на іх.

Чытай яшчэ:   СТВАРЫЦЬ НОВЫ САЙТ НА TIMEWEB

высновы

Выкарыстанне розных відаў спасылак у Java дазваляе паменшыць верагоднасць ўзнікнення уцечак памяці (memory leaks),
а таксама павысіць эфектыўнасць выкарыстання памяці. Для рэалізацыі кэша можна выкарыстоўваць клас WeakHashMap.

Беражыце памяць!

Іван Каплін, Люты 2010.

дадатковая інфармацыя

Ліквідацыю уцечак памяці з дапамогай слабых спасылак:

http://www.ibm.com/developerworks/ru/library/j-jtp11225/index.html

Разуменне слабых спасылак:

http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html

Спасылкі на АПА выкарыстоўваюцца класаў:

http://java.sun.com/javase/6/docs/api/java/util/WeakHashMap.html

http://java.sun.com/javase/6/docs/api/java /lang/ref/Reference.html

http://java.sun.com/javase/6/docs/api/java/lang/ref/ReferenceQueue.html

http://java.sun.com/javase/6/docs /api/java/lang/ref/PhantomReference.html

http://java.sun.com/javase/6/docs/api/java/lang/ref/WeakReference.html

http://java.sun.com/javase /6/docs/api/java/lang/ref/SoftReference.html

ПЕРАДАЧА ПАРАМЕТРАЎ У JAVA. - JAVARUSH

Пераменная ў Java - гэта кантэйнер, са значэннем ў ім. Так што значыць «перадаць» зменную? І якая розніца паміж прымітыўнымі і спасылкавых тыпамі дадзеных.

Мы дабяромся да гэтага пазней. Давайце спачатку пачнём з простага прысвойвання. Што робіць дадзены код:

int х = 3;
int у = х;


У радку 1 ствараецца пераменная x тыпу int і ёй прысвойваецца значэнне 3.
У радку 2, ствараецца пераменная y тыпу int і ёй присвается значэнне зменнай x .

У далейшым пераменная x ніяк не ўплывае на y . Java капіюе значэнне х (3) і змяшчае гэтую копію ў ў .

Гэта перадача параметру па значэнні. Вы не запісваеце адну зменную ў іншую. Значэнне капіюецца і прысвойваецца новай зменнай.

Выраз у = х; НЕ азначае «запісаць x у y «. Яно азначае «скапіяваць значэнне ўнутры х і запісаць гэтую копію ў ў «.

Калі пазней я змяню y :

у = 34;

Ці паўплывае гэта на x ? Канешне не. x па ранейшаму мае значэнне 3.

Калі пазней я змяню х :

х = 90;

Як гэта адаб'ецца на y ? Ніяк. Яны ніяк не звязаныя пасля таго, як было зроблена прысваенне (капіявання значэння).

А што наконт спасылкавых тыпаў? Як яны працуюць?

Не так ужо складана, на самой справе гэта правіла тое ж самае. Спасылкі робяць тое ж самае - вы атрымліваеце копію спасылкі.

Так што, калі я кажу:

Cat A = new Cat ();
Cat B = A;

Спасылка А капіюецца ў спасылку B . Да аб'екту гэта не ставіцца - у вас па ранейшаму ўсяго адзін аб'ект.
Але зараз у вас ёсць дзве розных спасылкі, што кантралююць адзін і той жа аб'ект Cat .

Зараз давайце разгледзім перадачу параметраў ў метады.

Java перадае параметры па значэнні. Заўсёды.
Гэта азначае - «скапіяваць значэнне і перадаць копію.»

Для прымітыўных тыпаў гэта лёгка:

int х = 5;
doStuff (х); / / Передать копию х (значение 5) в метод doStuff

Метад doStuff выглядае наступным чынам:

Чытай яшчэ:   ЯК ЗАХАВАЦЬ GOOGLE MAPS OFFLINE

void DoStuff (int у) {

/ / Действия с 'y'
}

Копія значэння x , тоесть 5, перадаецца ў метад doStuff () .

Метад doStuff () мае сваю ўласную зменную, якая называецца y .
Пераменная y - новая, іншая пераменная. З копіяй таго, што было ў х на момант перадачы яго ў метад. З гэтага моманту, у і х не ўплываюць адзін на аднаго. Пры змене ў , вы не закранаеце х .


void doStuff (int у) {

у = 27; / / Это не влияет на 'х'
}

І наадварот - пры змене х , вы не зменіце y . Адзінае што зрабіў x ў гэтай справе гэта скапіяваў сваё значэнне і перадаў яго ў метад doStuff () .

Як «перадача па значэнні» працуе са спасылкамі?

Занадта многія людзі кажуць, «Java перадае прымітыўныя тыпы па значэнні, а аб'екты па спасылцы». Гэта не так як кажуць. Java перадае ўсе па значэнні. З прымітывы, вы атрымліваеце копію змесціва. Са спасылкамі вы таксама атрымліваеце копію змесціва.

Але што такое змесціва спасылкі?

Пульт дыстанцыйнага кіравання. Сродкі для кіравання / доступу да аб'екта.

Калі вы перадаеце спасылку на аб'ект у метад, вы перадаеце копію спасылкі. Клон пульта дыстанцыйнага кіравання. Аб'ект усё яшчэ сядзіць у кучы дзе быў створаны, чакаючы кагосьці, каб выкарыстоўвалі пульт. Аб'ект не хвалюе колькі пультаў «запраграмаваныя» каб кантраляваць яго. Гэта хвалюе толькі зборшчыка смецця і вас, праграміста.

Таму, калі вы кажаце:

Cat A = new Cat ();
doStuff (А);

void DoStuff (Cat B) {

/ / Использование B
}

Існуе толькі адзін аб'ект Cat . Але цяпер два пульта кіравання (спасылкі) могуць атрымаць доступ да аднаго і таго ж аб'екту Cat .

Так што зараз усё, што B робіць аб'екту Cat , паўплывае на Cat , на які паказвае A , але гэта не паўплывае на змесціва A !

Вы можаце змяніць Cat , выкарыстоўваючы новую спасылку B (скапіяваную непасрэдна з А ), але вы не можаце змяніць А .

Якога чорта гэта значыць?

Вы можаце змяніць аб'ект, на які спасылаецца А , але вы не можаце ўзяць і змяніць спасылку А - пераадрасаваць яе на іншы аб'ект або null . Так што калі вы зменіце спасылку B (не сам аб'ект Cat на які спасылаецца B , а само значэнне спасылкі) вы не зменіце значэнне А . І наадварот.

Так што:

Cat A = new Cat ();
doStuff (А);

void doStuff (Cat B) {

B = new Cat (); / / Не повлияет на ссылку A
}

Гэта проста значыць, што B паказвае на іншы аб'ект. A па-ранейшаму шчаслівая.

Так што паўтарайце за мной:

Java перадае ўсе па значэнні.

(Добра, яшчэ раз ... з пачуццём.)

Java перадае ўсе па значэнні.

Для прымітыўных тыпаў - вы перадаеце копію бягучага значэння, для спасылак на аб'екты - вы перадаеце копію спасылкі (дыстанцыйнага кіравання). Вы ніколі не перадаеце аб'ект. Усе аб'екты захоўваюцца ў кучы. Заўсёды.

Цяпер заварыце здаравенную кубак кавы і пішыце код!

Арыгінал артыкула.

Чытай на іншых мовах

 беларускіанглійская Нямецкі іспанскі французскі італьянскі партугальская турэцкі арабская ўкраінскі шведскі венгерская балгарскі эстонскі Кітайскі (спрошчаны) в'етнамская румынская тайская славенская славацкая сербская малайская нарвежская латышская Літоўскі карэйская японскі інданезійская хіндзі іўрыт фінскі грэцкі нідэрландская чэшскі дацкая харвацкая Кітайскі (традыцыйны) тагальская урду Азейбарджанский армянскіпольскі бенгальская грузінскі казахскі каталонская Mongolski руская Таджитский Tamil'skij тэлугу Узбецкий

дадаць каментар

Ваш e-mail не будзе апублікаваны. Абавязковыя палі пазначаныя *