මට සම්මුඛ පරීක්ෂණයක් පැවැත්වූ අතර, ජාවා සමඟ මතක කාන්දුවක් ඇති කිරීමට මට නියම විය .
එය නිර්මාණය කිරීම ආරම්භ කරන්නේ කෙසේද යන්න පිළිබඳ කිසිදු හෝඩුවාවක් මට නොමැති බව අමුතුවෙන් කිව යුතු නැත.
උදාහරණයක් කුමක් විය හැකිද?
මට සම්මුඛ පරීක්ෂණයක් පැවැත්වූ අතර, ජාවා සමඟ මතක කාන්දුවක් ඇති කිරීමට මට නියම විය .
එය නිර්මාණය කිරීම ආරම්භ කරන්නේ කෙසේද යන්න පිළිබඳ කිසිදු හෝඩුවාවක් මට නොමැති බව අමුතුවෙන් කිව යුතු නැත.
උදාහරණයක් කුමක් විය හැකිද?
Answers:
පිරිසිදු ජාවා තුළ සත්ය මතක කාන්දුවක් (කේත ධාවනය කිරීමෙන් ප්රවේශ කළ නොහැකි නමුත් තවමත් මතකයේ ගබඩා කර ඇති) නිර්මාණය කිරීමට හොඳ ක්රමයක් මෙන්න:
ClassLoader
.new byte[1000000]
), ස්ථිතික ක්ෂේත්රයක් තුළ ඒ පිළිබඳව ප්රබල සඳහනක් ගබඩා කරයි, පසුව තමා වෙත යොමු කිරීමක් a ThreadLocal
. අමතර මතකය වෙන් කිරීම විකල්පයකි (පන්ති අවස්ථාව කාන්දු වීම ප්රමාණවත්ය), නමුත් එය කාන්දු වීම වඩා වේගවත් කරයි.ClassLoader
එය පටවා ඇති සියලුම යොමු කිරීම් ඉවත් කරයි.ThreadLocal
ඔරකල්හි ජේඩීකේ හි ක්රියාත්මක වන ආකාරය නිසා මෙය මතක කාන්දුවක් ඇති කරයි:
Thread
පුද්ගලික ක්ෂේත්රයක් ඇත threadLocals
, එය ඇත්ත වශයෙන්ම නූල්-දේශීය අගයන් ගබඩා කරයි.ThreadLocal
වස්තුවක් පිළිබඳ දුර්වල සඳහනක් වන අතර එම නිසා එම ThreadLocal
වස්තුව කසළ එකතු කිරීමෙන් පසුව එහි ප්රවේශය සිතියමෙන් ඉවත් කරනු ලැබේ.ThreadLocal
එහි යතුර වන වස්තුව වෙත යොමු කළ විට, එම වස්තුව කුණු එකතු කිරීම හෝ නූල් ජීවත්වන තාක් සිතියමෙන් ඉවත් නොකෙරේ.මෙම උදාහරණයේ දී, ශක්තිමත් යොමු දාමය මේ ආකාරයෙන් පෙනේ:
Thread
වස්තුව → threadLocals
සිතියම example උදාහරණ පන්තියේ උදාහරණ → උදාහරණ පන්තිය → ස්ථිතික ThreadLocal
ක්ෂේත්ර → ThreadLocal
වස්තුව.
( ClassLoader
කාන්දුව නිර්මාණය කිරීමේදී ඇත්ත වශයෙන්ම කාර්යභාරයක් ඉටු නොකරයි, මෙම අතිරේක යොමු දාමය නිසා එය කාන්දුව වඩාත් නරක අතට හැරේ: උදාහරණ පන්තිය ClassLoader
it it එය පටවා ඇති සියලුම පන්ති. බොහෝ ජේවීඑම් ක්රියාත්මක කිරීමේදී එය ඊටත් වඩා දරුණු විය ජාවා 7, පංති සහ පන්ති ClassLoader
කෙලින්ම පර්මජන් වලට වෙන් කර ඇති අතර ඒවා කිසි විටෙකත් කසළ එකතු කර නොතිබුණි.)
මෙම රටාවේ විචල්යතාවයක් වන්නේ යෙදුම් බහාලුම් (ටොම්කාට් වැනි) ඔබ නිතර භාවිතා කරන යෙදුම් නැවත නැවත යෙදවීම නම් පෙරනයක් වැනි මතකයක් කාන්දු විය හැක්කේ මන්ද යන්නයි ThreadLocal
. මෙය සියුම් හේතු ගණනාවක් නිසා සිදුවිය හැකි අතර බොහෝ විට එය නිදොස් කිරීම සහ / හෝ නිවැරදි කිරීම දුෂ්කර ය.
යාවත්කාලීන කිරීම : බොහෝ අය එය දිගටම ඉල්ලා සිටින හෙයින්, මෙම හැසිරීම ක්රියාවෙන් පෙන්වන උදාහරණ කේතයක් මෙන්න .
ස්ථිතික ක්ෂේත්ර රඳවා ගැනීමේ වස්තු යොමු [esp අවසන් ක්ෂේත්රය]
class MemorableClass {
static final ArrayList list = new ArrayList(100);
}
String.intern()
දිගු නූල් ඇමතීම
String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();
(අනාවරණය නොකළ) විවෘත ධාරාවන් (ගොනුව, ජාලය ආදිය ...)
try {
BufferedReader br = new BufferedReader(new FileReader(inputFile));
...
...
} catch (Exception e) {
e.printStacktrace();
}
විවෘත නොකළ සම්බන්ධතා
try {
Connection conn = ConnectionFactory.getConnection();
...
...
} catch (Exception e) {
e.printStacktrace();
}
ජේවීඑම් හි කසළ එකතු කරන්නාගෙන් ළඟා විය නොහැකි ප්රදේශ , එනම් ස්වදේශීය ක්රම මගින් මතකය වෙන් කිරීම
වෙබ් යෙදුම් වලදී, යෙදුම පැහැදිලිවම නවත්වන තුරු හෝ ඉවත් කරන තුරු සමහර වස්තු යෙදුම් විෂය පථයේ ගබඩා වේ.
getServletContext().setAttribute("SOME_MAP", map);
වැරදි හෝ නුසුදුසු JVM විකල්ප , එනම් noclassgc
IBM JDK හි ඇති විකල්පය, භාවිතයට නොගත් පන්ති කසළ එකතු කිරීම වළක්වයි
IBM jdk සැකසුම් බලන්න .
close()
සාමාන්යයෙන් finalizer දී පළ කළහ නැත නූල් සිට අවහිර කිරීමේ ක්රියාවක් විය හැක). වසා නොදැමීම නරක පුරුද්දකි, නමුත් එය කාන්දුවක් ඇති නොකරයි. අනාවරණය නොකළ java.sql.Connection එක සමානයි.
intern
හැෂ් ටේබල් අන්තර්ගතය පිළිබඳ දුර්වල සඳහනක් පමණක් ඇති බවයි . වැනි, එය වේ කුණු නිසි නොව කාන්දුවක් රැස් කළේය. (නමුත් IANAJP) mindprod.com/jgloss/interned.html#GC
කළ යුතු සරලම දෙය නම් වැරදි (හෝ නොපවතින) සහිත හැෂ්සෙට් එකක් භාවිතා කිරීම hashCode()
හෝ equals()
ඉන්පසු “අනුපිටපත්” එකතු කිරීම ය. අනුපිටපත් නොසලකා හැරීම වෙනුවට, කට්ටලය කවදා හෝ වර්ධනය වන අතර ඔබට ඒවා ඉවත් කිරීමට නොහැකි වනු ඇත.
ඔබට මෙම නරක යතුරු / අංග වටා රැඳී සිටීමට අවශ්ය නම් ඔබට වැනි ස්ථිතික ක්ෂේත්රයක් භාවිතා කළ හැකිය
class BadKey {
// no hashCode or equals();
public final String key;
public BadKey(String key) { this.key = key; }
}
Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.
පහත දැක්වෙන්නේ ජාවා කාන්දු වන, අමතක වූ සවන්දෙන්නන්ගේ සම්මත අවස්ථාව, ස්ථිතික යොමු කිරීම්, හැෂ්මැප් වල ව්යාජ / වෙනස් කළ හැකි යතුරු හෝ ඔවුන්ගේ ජීවන චක්රය අවසන් කිරීමට කිසිදු අවස්ථාවක් නොමැතිව සිරවී ඇති නූල් පමණි.
File.deleteOnExit()
- සෑම විටම නූල් කාන්දු වේ, char[]
, එබැවින් පසුකාලීනව අදාළ නොවේ ; An ඩැනියෙල්, ඡන්ද අවශ්ය නැත.බොහෝ විට කළමනාකරණය නොකළ නූල් වල අන්තරාය පෙන්වීමට මම නූල් කෙරෙහි අවධානය යොමු කරමි, පැද්දීම ස්පර්ශ කිරීමට පවා කැමති නැත.
Runtime.addShutdownHook
ඉවත් නොකෙරේ ... පසුව ඉවත් කරන්න ෂට්ඩවුන්හූක් සමඟ පවා ආරම්භ නොකළ නූල් සම්බන්ධව ThreadGroup පන්තියේ දෝෂයක් නිසා එය එකතු නොවිය හැකි අතර ThreadGroup effectively ලදායී ලෙස කාන්දු වේ. ගොසිප් රවුටරයේ කාන්දු වීම JGroup සතුව ඇත.
නිර්මාණය කිරීම, නමුත් ආරම්භ නොකිරීම, Thread
ඉහත කාණ්ඩයටම අයත් වේ.
නූල් නිර්මාණය වන උරුම ContextClassLoader
සහ AccessControlContext
, එකතු ThreadGroup
ඕනෑම InheritedThreadLocal
, සියලු දෙනා යොමු සමස්ත පන්ති classloader රුවා සහ සියලු ස්ථිතික යොමු, හා, ජා-ජා සමඟ හැකි කාන්දු වේ. සුපිරි සරල ThreadFactory
අතුරුමුහුණතක් සහිත සමස්ත jucExecutor රාමුව සමඟ එහි බලපෑම විශේෂයෙන් දැකිය හැකි නමුත් බොහෝ සංවර්ධකයින්ට සැඟවී ඇති අන්තරාය පිළිබඳ කිසිදු හෝඩුවාවක් නොමැත. බොහෝ පුස්තකාලයන් ඉල්ලීම පරිදි නූල් ආරම්භ කරයි (කර්මාන්තයේ ජනප්රිය පුස්තකාල ඕනෑවට වඩා).
ThreadLocal
හැඹිලි; ඒවා බොහෝ අවස්ථාවල නපුරු ය. ThreadLocal මත පදනම් වූ සරල හැඹිලි සියල්ලම සෑම කෙනෙකුම දැක ඇති බව මට විශ්වාසයි, නරක ආරංචිය: ක්ලැස්ලෝඩර් සන්දර්භය තුළ අපේක්ෂිත ජීවිතයට වඩා නූල් දිගින් දිගටම යන්නේ නම්, එය පිරිසිදු කුඩා කාන්දුවකි. ඇත්ත වශයෙන්ම අවශ්ය නම් මිස ThreadLocal හැඹිලි භාවිතා නොකරන්න.
ඇමතුම් ThreadGroup.destroy()
මෙම ThreadGroup කිසිදු නූල් ම වන විට, නමුත් එය තවමත් ළමා ThreadGroups කරයි. ThreadGroup එහි මවුපියන්ගෙන් ඉවත් කිරීම වළක්වන නරක කාන්දුවක්, නමුත් සියලුම දරුවන් ගණන් කළ නොහැකි තත්වයට පත්වේ.
WeakHashMap භාවිතා කිරීම සහ අගය (in) මඟින් යතුර යොමු කරයි. ගොඩවල් නොමැතිව සොයා ගැනීමට මෙය අපහසු දෙයකි. Weak/SoftReference
ආරක්ෂිත වස්තුව වෙත නැවත යොමු කිරීමක් කළ හැකි දීර් extended කරන ලද සියල්ලටම එය අදාළ වේ .
java.net.URL
HTTP (S) ප්රොටෝකෝලය සමඟ භාවිතා කිරීම සහ (!) වෙතින් සම්පත් පූරණය කිරීම. මෙය විශේෂයි, KeepAliveCache
ThreadGroup පද්ධතියේ නව නූලක් නිර්මාණය කරයි, එය වත්මන් නූල් වල සන්දර්භය පන්තියේ පැටවුම කාන්දු කරයි. සජීවී නූල් නොමැති විට පළමු ඉල්ලීම මත නූල් සාදනු ලැබේ, එබැවින් ඔබට වාසනාවන්ත විය හැකිය හෝ කාන්දු විය හැකිය. ජාවා 7 හි කාන්දුව දැනටමත් සවි කර ඇති අතර නූල් සාදන කේතය නිසි ලෙස සන්දර්භය පන්ති භාරය ඉවත් කරයි. තවත් අවස්ථා කිහිපයක් තිබේ (ImageFetcher වගේ, ද ස්ථාවර ) සමාන නූල් සෑදීම.
ඉදිකිරීම්කරු තුළ InflaterInputStream
ගමන් කිරීම භාවිතා new java.util.zip.Inflater()
කිරීම ( PNGImageDecoder
නිදසුනක් ලෙස) සහ end()
ගිනි අවුලුවන ඇමතුම නොකිරීම. හොඳයි, ඔබ ඉදිකිරීම්කරු තුළ සාධාරණ ලෙස සමත් වුවහොත් new
කිසිදු අවස්ථාවක් නැත ... ඔව්, close()
ධාරාව ඇමතීමෙන් එය පාලක පරාමිතිය ලෙස අතින් සම්මත වුවහොත් එය පුපුරා යන්නේ නැත. මෙය සත්ය කාන්දුවීමක් නොවන බැවින් එය අවසන් කරන්නා විසින් මුදා හරිනු ඇත ... එය අවශ්ය යැයි හැඟෙන විට. ඒ මොහොත දක්වාම එය ස්වදේශික මතකය ඉතා නරක ලෙස අනුභව කරන අතර එය ලිනක්ස් oom_killer ද unity ුවම් නොලබා ක්රියාවලිය විනාශ කිරීමට හේතු වේ. ප්රධාන ගැටළුව වන්නේ ජාවා හි අවසන් කිරීම ඉතා විශ්වාසදායක නොවන අතර G1 එය 7.0.2 වන තෙක් නරක අතට හැරීමයි. කතාවේ සදාචාරය: ඔබට හැකි ඉක්මනින් දේශීය සම්පත් නිදහස් කරන්න; අවසන් කරන්නා ඉතා දුර්වලයි.
එකම නඩුව java.util.zip.Deflater
. ජාවා හි ඩෙෆ්ලේටර් මතක කුසගින්නෙන් පෙළෙන බැවින් මෙය වඩාත් නරක ය, එනම් සෑම විටම බිටු 15 ක් (උපරිම) සහ මතක මට්ටම් 8 ක් (9 උපරිම වේ) ස්වදේශීය මතකයේ කිලෝ සිය ගණනක් වෙන් කරයි. වාසනාවකට මෙන්, Deflater
බහුලව භාවිතා නොවන අතර මගේ දැනුමට අනුව JDK හි කිසිදු අනිසි භාවිතයක් නොමැත. end()
ඔබ අතින් Deflater
හෝ නිර්මාණය කරන්නේ නම් සැමවිටම අමතන්න Inflater
. අන්තිම දෙකේ හොඳම කොටස: ලබා ගත හැකි සාමාන්ය පැතිකඩ මෙවලම් හරහා ඔබට ඒවා සොයාගත නොහැක.
(ඉල්ලීම පරිදි මට හමු වූ තවත් කාලය නාස්ති කිහිපයක් එකතු කළ හැකිය.)
වාසනාව සහ ආරක්ෂිතව සිටින්න; කාන්දුවීම් නපුරු ය!
Creating but not starting a Thread...
අහෝ, ශතවර්ෂ කිහිපයකට පෙර මට මෙය දරුණු ලෙස දෂ්ට විය! (ජාවා 1.3)
unstarted
ගණනය කිරීම වැඩි කරනවා පමණක් නොව, නූල් කණ්ඩායම විනාශ කිරීමෙන් වළක්වයි (අඩු නපුර නමුත් තවමත් කාන්දුවක්)
ThreadGroup.destroy()
ThreadGroup හි නූල් නොමැති විට ඇමතීම ..." ඇදහිය නොහැකි තරම් සියුම් දෝෂයකි; මම මෙය පැය ගණනක් ලුහුබඳිමින්, නොමඟ ගියෙමි, මන්ද මගේ පාලනයේ නූල් ගණනය කිරීමෙන් GUI කිසිවක් පෙන්වූයේ නැත, නමුත් නූල් කණ්ඩායම සහ, අවම වශයෙන් එක් ළමා කණ්ඩායමක්වත් නොයනු ඇත.
මෙහි බොහෝ උදාහරණ "ඉතා සංකීර්ණ" ය. ඒවා අද්දර නඩු. මෙම උදාහරණ සමඟ, ක්රමලේඛකයා වැරැද්දක් කර ඇත (සමාන / හැෂ්කෝඩ් නැවත අර්ථ දැක්වීම වැනි), හෝ ජේවීඑම් / ජාවා (ස්ථිතික සමඟ පන්තියේ බර ...) හි මුල්ලක නඩුවකින් දෂ්ට කර ඇත. මම හිතන්නේ එය සම්මුඛ පරීක්ෂකවරයකුට අවශ්ය ආදර්ශය හෝ වඩාත් පොදු අවස්ථාව නොවේ.
නමුත් මතක කාන්දුවීම් සඳහා වඩාත් සරල අවස්ථා තිබේ. කසළ එකතු කරන්නා නිදහස් කරන්නේ තවදුරටත් සඳහන් නොකරන දේ පමණි. ජාවා සංවර්ධකයින් වන අපි මතකය ගැන තැකීමක් නොකරමු. අවශ්ය විටෙක අපි එය වෙන් කර ස්වයංක්රීයව නිදහස් වීමට ඉඩ දෙමු. හොඳයි.
නමුත් ඕනෑම දිගුකාලීන යෙදුමක් හවුල් තත්වයට පත්වේ. එය ඕනෑම දෙයක් විය හැකිය, සංඛ්යාන, සිංගල්ටන් ... බොහෝ විට සුළු නොවන යෙදුම් සංකීර්ණ වස්තු ප්රස්ථාර සෑදීමට නැඹුරු වේ. එකතුවක් වෙතින් එක් වස්තුවක් ඉවත් කිරීමට ශුන්ය හෝ බොහෝ විට අමතක කිරීමට අමතක කිරීම මතක කාන්දු වීමක් සඳහා ප්රමාණවත් වේ.
ඇත්ත වශයෙන්ම සියලු ආකාරයේ සවන්දෙන්නන් (UI සවන්දෙන්නන් වැනි), හැඹිලි හෝ දිගු කාලීනව බෙදාගත් ඕනෑම රාජ්යයක් නිසි ලෙස හසුරුවන්නේ නැත්නම් මතක කාන්දු වීමක් ඇති කරයි. තේරුම් ගත යුතු දෙය නම් මෙය ජාවා කෝනර් නඩුවක් හෝ කසළ එකතු කරන්නාගේ ගැටලුවක් නොවන බවයි. එය නිර්මාණ ගැටළුවක්. අපි දිගු කාලීන වස්තුවකට සවන්දෙන්නෙකු එක් කරන ලෙස අපි සැලසුම් කරමු, නමුත් තවදුරටත් අවශ්ය නොවන විට අපි සවන්දෙන්නන් ඉවත් නොකරමු. අපි වස්තු හැඹිලි, නමුත් ඒවා හැඹිලියෙන් ඉවත් කිරීමට අපට උපාය මාර්ගයක් නොමැත.
ගණනය කිරීමකින් අවශ්ය වන පෙර තත්වය ගබඩා කරන සංකීර්ණ ප්රස්ථාරයක් අප සතුව ඇත. නමුත් පෙර පැවති රාජ්යය ඊට පෙර සහ එසේ රාජ්යයට සම්බන්ධ වේ.
හරියට අපි SQL සම්බන්ධතා හෝ ගොනු වසා දැමිය යුතුයි. එකතුවෙන් මූලද්රව්ය ශුන්ය කිරීමට සහ ඉවත් කිරීමට අපට නිසි යොමු කිරීම් අවශ්ය වේ. අපට නිසි හැඹිලි ක්රමෝපායන් ඇත (උපරිම මතක ප්රමාණය, මූලද්රව්ය ගණන හෝ ටයිමර). සවන්දෙන්නෙකුට දැනුම් දීමට ඉඩ දෙන සියලුම වස්තූන් ඇඩ්ලිස්ටනර් සහ ඉවත් කිරීමේ ලැයිස්තුව යන දෙකම සැපයිය යුතුය. මෙම දැනුම්දීම් තවදුරටත් භාවිතා නොකරන විට, ඔවුන් ඔවුන්ගේ සවන්දෙන්නන්ගේ ලැයිස්තුව ඉවත් කළ යුතුය.
මතක කාන්දුවක් සැබවින්ම සැබවින්ම හැකි අතර එය සම්පූර්ණයෙන්ම පුරෝකථනය කළ හැකිය. විශේෂ භාෂා විශේෂාංග හෝ මුල්ලක අවස්ථා අවශ්ය නොවේ. මතක කාන්දුවීම් යනු යමක් නැතිවී ඇති බවට හෝ නිර්මාණ ගැටළු ඇති බවට දර්ශකයකි.
WeakReference
) එක සිට අනෙකට. වස්තු යොමු
PhantomReference
වස්තුවක් ඒ ගැන සැලකිලිමත් වන කිසිවෙකු නොමැති බව සොයාගත හොත් (ඒ හා සමාන ආකාරයකින් ) පද්ධතියට දැනුම් දීම් ලබා දෙන්න . WeakReference
තරමක් සමීප වන නමුත් එය භාවිතා කිරීමට පෙර එය ශක්තිමත් යොමුවකට පරිවර්තනය කළ යුතුය; ප්රබල යොමුව පවතින අතර GC චක්රයක් සිදුවුවහොත්, ඉලක්කය ප්රයෝජනවත් යැයි උපකල්පනය කෙරේ.
පිළිතුර මුළුමනින්ම රඳා පවතින්නේ සම්මුඛ පරීක්ෂකවරයා ඔවුන් අසන්නේ යැයි සිතූ දේ මත ය.
ජාවා කාන්දු වීම ප්රායෝගිකව කළ හැකිද? ඇත්ත වශයෙන්ම එය එසේ වන අතර අනෙක් පිළිතුරු වල උදාහරණ ඕනෑ තරම් තිබේ.
නමුත් මෙටා ප්රශ්න කිහිපයක් අසනු ලැබිය හැකිද?
මම ඔබේ මෙටා ප්රශ්නය කියවන්නේ "මෙම සම්මුඛ පරීක්ෂණයේදී මට භාවිතා කළ හැකි පිළිතුර කුමක්ද" යන්නයි. ඒ නිසා මම ජාවා වෙනුවට සම්මුඛ පරීක්ෂණ කුසලතා කෙරෙහි අවධානය යොමු කරන්නෙමි. සම්මුඛ පරීක්ෂණයකදී ඔබ ජාවා කාන්දු කරන්නේ කෙසේදැයි දැන ගැනීමට අවශ්ය ස්ථානයක සිටීමට වඩා සම්මුඛ සාකච්ඡාවකදී ප්රශ්නයකට පිළිතුර නොදැනීමේ තත්වය ඔබ නැවත නැවත කිරීමට ඉඩ ඇතැයි මම විශ්වාස කරමි. එබැවින්, මෙය උපකාරී වනු ඇතැයි අපේක්ෂා කරමු.
සම්මුඛ පරීක්ෂණ සඳහා ඔබට වර්ධනය කළ හැකි වැදගත්ම කුසලතාවන් වන්නේ ප්රශ්නවලට සක්රියව ඇහුම්කන් දීමට ඉගෙන ගැනීම සහ සම්මුඛ පරීක්ෂක සමඟ ඔවුන්ගේ අභිප්රාය උපුටා ගැනීම සඳහා වැඩ කිරීමයි. මෙය ඔවුන්ගේ ප්රශ්නයට ඔවුන්ට අවශ්ය ආකාරයට පිළිතුරු සැපයීමට පමණක් නොව, ඔබට වැදගත් සන්නිවේදන කුසලතා ඇති බව පෙන්වයි. බොහෝ සමාන දක්ෂතා ඇති සංවර්ධකයින් අතර තේරීමකට එළඹෙන විට, ඔවුන් සෑම අවස්ථාවකදීම ප්රතිචාර දැක්වීමට පෙර සවන් දෙන, සිතන සහ තේරුම් ගන්නා තැනැත්තා මම බඳවා ගන්නෙමි.
ඔබට ජේඩීබීසී තේරෙන්නේ නැත්නම් පහත දැක්වෙන්නේ අර්ථ විරහිත උදාහරණයකි . හෝ JDBC සමීප කිරීමට සංවර්ධක අපේක්ෂා ආකාරය අවම වශයෙන් Connection
, Statement
හා ResultSet
ක්රියාත්මක කිරීම මත රඳා ඔවුන් උපයෝගි හෝ ඔවුන්ට යොමු අහිමි, ඒ වෙනුවට පෙර අවස්ථා finalize
.
void doWork()
{
try
{
Connection conn = ConnectionFactory.getConnection();
PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query
ResultSet rs = stmt.executeQuery();
while(rs.hasNext())
{
... process the result set
}
}
catch(SQLException sqlEx)
{
log(sqlEx);
}
}
ඉහත සඳහන් ගැටළුව නම්, Connection
වස්තුව වසා නොමැති අතර, එම නිසා කසළ එකතු කරන්නා අවට පැමිණ එය ළඟා විය නොහැකි බව දකින තුරු භෞතික සම්බන්ධතාවය විවෘතව පවතිනු ඇත. GC විසින් මෙම finalize
ක්රමයට ආයාචනා කරනු ඇත , නමුත් ක්රියාත්මක නොකරන JDBC ධාවක ඇත finalize
, අවම වශයෙන් Connection.close
ක්රියාත්මක වන ආකාරයටම නොවේ. එහි ප්රති behavior ලයක් ලෙස ළඟා විය නොහැකි වස්තූන් නිසා මතකය නැවත ගොඩ ගන්නා අතර, Connection
වස්තුව හා සම්බන්ධ සම්පත් (මතකය ද ඇතුළුව) නැවත ගොඩ නොගනු ඇත.
කොහෙද එවැනි අවස්ථාවක දී Connection
ඇති finalize
ක්රමය සියල්ල පිරිසිදු නැත, එක් ඇත්තටම දත්ත සමුදා සේවාදායකයට භෞතික සම්බන්ධයක් නම් එය (එම දත්ත සමුදා සේවාදායකය අවසානයේ ඇති සම්බන්ධය ජීවත් නොවන බව සංඛ්යා තෙක්, කැළි කසළ පැදි කීපයක් ගත වන බව ඔබට සිතෙන්න පුළුවන් ), සහ වසා දැමිය යුතුය.
ජේඩීබීසී රියදුරු ක්රියාත්මක finalize
කළත්, අවසන් කිරීමේ දී ව්යතිරේකයන් විසි කළ හැකිය. එහි ප්රති behavior ලයක් ලෙස දැන් “අක්රිය” වස්තුව හා සම්බන්ධ ඕනෑම මතකයක් නැවත ලබා නොගනු ඇත finalize
.
වස්තු අවසන් කිරීමේදී ව්යතිරේකවලට මුහුණ දීමේ ඉහත සිදුවීම මතක කාන්දුවකට තුඩු දිය හැකි තවත් සිදුවීමක් හා සම්බන්ධ වේ - වස්තු නැවත නැඟිටීම. වස්තු නැවත නැඟිටීම බොහෝ විට සිදු කරනුයේ වෙනත් වස්තුවකින් අවසාන වස්තුවෙන් ප්රබල සඳහනක් නිර්මාණය කිරීමෙනි. වස්තු නැවත නැඟිටීම අනිසි ලෙස භාවිතා කරන විට එය මතක කාන්දුවීම් වලට හේතු වේ.
ඔබට උපකල්පනය කළ හැකි තවත් බොහෝ උදාහරණ තිබේ - වැනි
List
ඔබ ලැයිස්තුවට පමණක් එකතු කරන සහ එයින් මකා නොදමන අවස්ථාවක් (ඔබට තවදුරටත් අවශ්ය නොවන අංග ඉවත් කළ යුතු වුවද), හෝSocket
හෝFile
s , නමුත් ඒවා තවදුරටත් අවශ්ය නොවන විට ඒවා වසා නොදැමීම ( Connection
පන්තිය සම්බන්ධ ඉහත උදාහරණයට සමානය ).Connection.close
මගේ සියලුම SQL ඇමතුම් අවසාන වාරණයට ඇතුළත් කරන තෙක් මම SQL දත්ත සමුදායන්ගේ සම්බන්ධතා සීමාවන්ට පහර දුන්නා . අමතර විනෝදය සඳහා මම දිගු කාලීනව ක්රියාත්මක වන ඔරකල් ගබඩා කළ ක්රියා පටිපාටි කිහිපයක් ඇමතුවෙමි.
විභව මතක කාන්දුවක් සඳහා සරලම උදාහරණයන්ගෙන් එකක් වන අතර එය වළක්වා ගන්නේ කෙසේද යන්න ArrayList.remove (int) ක්රියාත්මක කිරීම වේ:
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // (!) Let gc do its work
return oldValue;
}
ඔබ එය ඔබම ක්රියාත්මක කරන්නේ නම්, තවදුරටත් භාවිතා නොකරන ( elementData[--size] = null
) අරාව මූලද්රව්යය ඉවත් කිරීමට ඔබ සිතුවාද ? එම සඳහන මගින් විශාල වස්තුවක් පණපිටින් තබා ගත හැකිය ...
ඔබට තවදුරටත් මතක මතක කාන්දු වීමක් අවශ්ය නොවන වස්තූන් වෙත යොමු කිරීම් කරන ඕනෑම වේලාවක. ජාවා වැඩසටහන් වල මතක කාන්දුවීම් හැසිරවීම ජාවා තුළ මතක කාන්දු වන ආකාරය සහ ඒ සඳහා ඔබට කළ හැකි දේ පිළිබඳ උදාහරණ බලන්න .
...then the question of "how do you create a memory leak in X?" becomes meaningless, since it's possible in any language.
ඔබ එම නිගමනයට එළඹෙන්නේ කෙසේදැයි මට නොපෙනේ. ඇත අඩු ඕනෑම නිර්වචනය විසින් ජාවා මතක කාන්දුවක් නිර්මාණය කිරීම සඳහා ක්රම. එය අනිවාර්යයෙන්ම තවමත් වලංගු ප්රශ්නයකි.
Sun.misc.Unsafe පන්තිය සමඟ මතක කාන්දු වීමට ඔබට හැකිය . ඇත්ත වශයෙන්ම මෙම සේවා පන්තිය විවිධ සම්මත පන්තිවල භාවිතා වේ (උදාහරණයක් ලෙස java.nio පන්තිවල). ඔබට මෙම පංතියේ නිදසුන කෙලින්ම නිර්මාණය කළ නොහැක , නමුත් ඔබට එය කිරීමට පරාවර්තනය භාවිතා කළ හැකිය .
කේතය සූර්යග්රහණ IDE හි සම්පාදනය නොකරයි - විධානය භාවිතා කර එය javac
සම්පාදනය කරන්න (සම්පාදනය කිරීමේදී ඔබට අනතුරු ඇඟවීම් ලැබෙනු ඇත)
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class TestUnsafe {
public static void main(String[] args) throws Exception{
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
System.out.print("4..3..2..1...");
try
{
for(;;)
unsafe.allocateMemory(1024*1024);
} catch(Error e) {
System.out.println("Boom :)");
e.printStackTrace();
}
}
}
මට මගේ පිළිතුර මෙතැනින් පිටපත් කළ හැකිය: ජාවා හි මතකය කාන්දු වීමට පහසුම ක්රමය?
"පරිගණක විද්යාවේ (හෝ කාන්දු වීම, මෙම සන්දර්භය තුළ) මතක කාන්දුවක් සිදුවන්නේ පරිගණක වැඩසටහනක් මතකය පරිභෝජනය කරන නමුත් එය නැවත මෙහෙයුම් පද්ධතියට මුදා හැරීමට නොහැකි වූ විටය." (විකිපීඩියා)
පහසුම පිළිතුර: ඔබට බැහැ. ජාවා ස්වයංක්රීය මතක කළමනාකරණය කරන අතර ඔබට අවශ්ය නොවන සම්පත් නිදහස් කරයි. ඔබට මෙය සිදුවීම වැළැක්විය නොහැක. එය සෑම විටම සම්පත් නිදහස් කිරීමට හැකි වනු ඇත. අතින් මතක කළමනාකරණය සහිත වැඩසටහන් වලදී මෙය වෙනස් වේ. Malloc () භාවිතා කර ඔබට C හි යම් මතකයක් ලබා ගත නොහැක. මතකය නිදහස් කිරීම සඳහා, ඔබට malloc ආපසු පැමිණි දර්ශකය අවශ්ය වන අතර ඒ මත නොමිලේ () අමතන්න. නමුත් ඔබට තවදුරටත් දර්ශකය නොමැති නම් (නැවත ලිවීම හෝ ජීවිත කාලය ඉක්මවා), එවිට ඔබට අවාසනාවකට මෙම මතකය නිදහස් කිරීමට නොහැකි වන අතර එමඟින් ඔබට මතක කාන්දුවක් ඇත.
මේ දක්වා ඇති අනෙක් සියලුම පිළිතුරු මගේ අර්ථ දැක්වීමේදී ඇත්ත වශයෙන්ම මතක කාන්දු නොවේ. ඔවුන් සියල්ලන්ම මතකය අර්ථ විරහිත දේවලින් වේගයෙන් පුරවා ගැනීම අරමුණු කරයි. නමුත් ඕනෑම වේලාවක ඔබ විසින් නිර්මාණය කරන ලද වස්තූන් අවලංගු කර මතකය නිදහස් කර ගත හැකිය -> නැත. කසළ එකතු කරන්නාට නිමක් නැති පුඩුවක් තුළට බලහත්කාරයෙන් කඩා වැටීම සඳහා ඔහුගේ විසඳුම effectively ලදායී බැවින් මා පිළිගත යුතු බැවින් ඇකෝන්රාඩ්ගේ පිළිතුර ඉතා සමීප වේ.
දීර් answer පිළිතුර නම්: JNI භාවිතා කර ජාවා සඳහා පුස්තකාලයක් ලිවීමෙන් ඔබට මතක කාන්දුවක් ලබා ගත හැකි අතර එමඟින් අතින් මතක කළමනාකරණය සහ මතක කාන්දු විය හැකිය. ඔබ මෙම පුස්තකාලය අමතන්නේ නම්, ඔබේ ජාවා ක්රියාවලිය මතකය කාන්දු වේ. නැතහොත්, ඔබට ජේවීඑම් හි දෝෂ තිබිය හැකිය, එවිට ජේවීඑම් මතකය නැති වේ. ජේවීඑම් හි බොහෝ විට දෝෂ තිබේ, කසළ එකතු කිරීම එතරම් සුළු දෙයක් නොවන නිසා දන්නා සමහරක් ඒවා තිබිය හැකිය, නමුත් එය තවමත් දෝෂයකි. සැලසුම අනුව මෙය කළ නොහැකිය. එවැනි දෝෂයක් මඟින් ක්රියාත්මක වන ජාවා කේතයක් ඔබ ඉල්ලා සිටිය හැකිය. කණගාටුයි, මම එකක් නොදන්නා අතර එය කෙසේ හෝ ඊළඟ ජාවා අනුවාදයේ දෝෂයක් නොවනු ඇත.
මෙන්න http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29 හරහා සරල / නපුරු එකක් .
public class StringLeaker
{
private final String muchSmallerString;
public StringLeaker()
{
// Imagine the whole Declaration of Independence here
String veryLongString = "We hold these truths to be self-evident...";
// The substring here maintains a reference to the internal char[]
// representation of the original string.
this.muchSmallerString = veryLongString.substring(0, 1);
}
}
උපස්ථරය මුල්, වඩා දිගු නූල් වල අභ්යන්තර නිරූපණයට යොමු වන නිසා, මුල් පිටපත මතකයේ රැඳේ. මේ අනුව, ඔබට ස්ට්රිං ලීකර් වාදනය කරන තාක් කල්, ඔබ තනි අක්ෂර මාලාවක් මත රඳවාගෙන සිටින බව ඔබ සිතුවත්, මුළු මුල් නූලම මතකයේ ඇත.
මුල් නූලට අනවශ්ය සඳහනක් ගබඩා කිරීමෙන් වළක්වා ගත හැකි ක්රමය නම් මේ වගේ දෙයක් කිරීමයි:
...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...
අමතර නරක සඳහා, ඔබට .intern()
උපස්ථරය ද විය හැකිය :
...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...
එසේ කිරීමෙන් ස්ට්රිංලීකර් උදාහරණය ඉවත දැමීමෙන් පසුව පවා මුල් දිගු නූල් සහ ව්යුත්පන්න උපස්ථරය මතකයේ රැඳෙනු ඇත.
muchSmallerString
( StringLeaker
වස්තුව විනාශ වන නිසා), දිගු නූල ද නිදහස් වේ. මම මතක කාන්දු වීම ලෙස හඳුන්වන්නේ ජේවීඑම් හි මෙම අවස්ථාවෙහිදී කිසි විටෙකත් නිදහස් කළ නොහැකි මතකයයි. කෙසේ වෙතත්, මතකය නිදහස් කර ගන්නේ කෙසේදැයි ඔබම පෙන්වා ඇත : this.muchSmallerString=new String(this.muchSmallerString)
. සැබෑ මතක කාන්දුවක් සමඟ, ඔබට කළ හැකි කිසිවක් නැත.
intern
අවස්ථාව "මතක කාන්දුවකට" වඩා "මතක විස්මයක්" විය හැකිය. .intern()
කෙසේ වෙතත්, උපස්ථරය නිසැකවම දිගු නූලට යොමු කිරීම ආරක්ෂා කර ගත හැකි සහ නිදහස් කළ නොහැකි තත්වයක් නිර්මාණය කරයි.
GUI කේතයේ මේ සඳහා පොදු උදාහරණයක් වන්නේ විජට් / සංරචකයක් නිර්මාණය කිරීම සහ යම් ස්ථිතික / යෙදුම් විෂය පථයකට සවන්දෙන්නෙකු එක් කිරීම සහ විජට් විනාශ වූ විට සවන්දෙන්නන් ඉවත් නොකිරීම ය. ඔබට මතක කාන්දුවක් ලැබෙනවා පමණක් නොව, ඔබ සිදුවීම් වලට සවන් දෙන ඕනෑම දෙයක්, ඔබේ පැරණි සවන්දෙන්නන් ද කැඳවනු ලැබේ.
ඕනෑම සර්වට් බහාලුමක් තුළ ධාවනය වන ඕනෑම වෙබ් යෙදුමක් ගන්න (ටොම්කාට්, ජැටි, ග්ලාස්ෆිෂ්, ඕනෑම දෙයක් ...). යෙදුම නැවත වරක් 10 හෝ 20 වතාවක් නැවත යෙදවීම (සේවාදායකයේ ස්වයංක්රීය ඩිරෙක්ටරියේ WAR ස්පර්ශ කිරීමට එය ප්රමාණවත් විය හැකිය.
කිසිවෙකු මෙය සත්ය වශයෙන්ම අත්හදා බලා නොමැති නම්, නැවත සේවයේ යෙදවීමෙන් පසු ඔබට OutOfMemoryError එකක් ලැබීමට ඇති ඉඩකඩ වැඩිය, මන්ද යෙදුම පසුව පිරිසිදු කිරීමට සැලකිලිමත් නොවූ බැවිනි. මෙම පරීක්ෂණය සමඟ ඔබේ සේවාදායකයේ දෝෂයක් පවා ඔබට සොයාගත හැකිය.
ගැටළුව වන්නේ, කන්ටේනරයේ ආයු කාලය ඔබේ යෙදුමේ ආයු කාලය වඩා දිගු වීමයි. ඔබේ යෙදුමේ වස්තූන් හෝ පංති සඳහා කන්ටේනරයට තිබිය හැකි සියලු යොමු කිරීම් කුණු එකතු කළ හැකි බවට ඔබ වග බලා ගත යුතුය.
ඔබගේ වෙබ් යෙදුමේ un න සේවා නියුක්තියෙන් නොනැසී පවතින්නේ එක් සඳහනක් නම්, අනුරූප පංති භාරකරු සහ එහි ප්රති ence ලයක් ලෙස ඔබේ වෙබ් යෙදුමේ සියලුම පංති කුණු එකතු කළ නොහැක.
ඔබගේ යෙදුම මඟින් ආරම්භ කරන ලද නූල්, ThreadLocal විචල්යයන්, ල ging ු-සටහන් එකතු කරන්නන් පන්ති භාරකරුවන්ගේ කාන්දුවීම් ඇති කරන සාමාන්ය සැකකරුවන් වේ.
සමහර විට JNI හරහා බාහිර ස්වදේශීය කේතය භාවිතා කිරීමෙන්ද?
පිරිසිදු ජාවා සමඟ, එය පාහේ කළ නොහැකි ය.
නමුත් එය "සම්මත" ආකාරයේ මතක කාන්දුවක් ගැන වන අතර, ඔබට තවදුරටත් මතකයට ප්රවේශ විය නොහැකි නමුත් එය තවමත් යෙදුමට අයත් වේ. ඔබට ඒ වෙනුවට භාවිතයට නොගත් වස්තූන් වෙත යොමු කිරීම් තබා ගත හැකිය, නැතහොත් ඒවා වසා නොගෙන ධාරාවන් විවෘත කරන්න.
PermGen සහ XML විග්රහ කිරීම සම්බන්ධයෙන් මට එක් වරක් “මතක කාන්දුවක්” තිබේ. සංසන්දනය වේගවත් කිරීම සඳහා අප භාවිතා කළ එක්ස්එම්එල් විග්රහකය (එය කුමක්දැයි මට මතක නැත) ටැග් නම් මත String.intern () කළා. අපගේ ගනුදෙනුකරුවකුට දත්ත අගයන් එක්ස්එම්එල් ගුණාංග හෝ පෙළෙහි නොව ටැග් නම් ලෙස ගබඩා කිරීමට හොඳ අදහසක් තිබුණි, එබැවින් අපට මෙවැනි ලේඛනයක් තිබේ:
<data>
<1>bla</1>
<2>foo</>
...
</data>
ඇත්ත වශයෙන්ම, ඔවුන් සංඛ්යා භාවිතා නොකළ නමුත් දිගු පෙළ හැඳුනුම්පත් (අක්ෂර 20 ක් පමණ) අද්විතීය වූ අතර ඒවා දිනකට මිලියන 10-15 අතර අනුපාතයකින් පැමිණියේය. එමඟින් දිනකට මෙගාබයිට් 200 ක් කුණු බවට පත් වන අතර එය නැවත කිසි විටෙකත් අවශ්ය නොවන අතර කිසි විටෙකත් ජී.සී.ඩී. (එය පර්ම්ජෙන් හි ඇති බැවින්). අපට පර්මජන් 512 MB ලෙස සකසා ඇති අතර, ඒ නිසා මතකයෙන් බැහැරව (OOME) පැමිණීමට දින දෙකක් පමණ ගත විය ...
මතක කාන්දුවක් යනු කුමක්ද:
සාමාන්ය උදාහරණය:
වස්තු හැඹිලිය යනු දේවල් අවුල් කිරීමට හොඳ ආරම්භයකි.
private static final Map<String, Info> myCache = new HashMap<>();
public void getInfo(String key)
{
// uses cache
Info info = myCache.get(key);
if (info != null) return info;
// if it's not in cache, then fetch it from the database
info = Database.fetch(key);
if (info == null) return null;
// and store it in the cache
myCache.put(key, info);
return info;
}
ඔබේ හැඹිලිය වැඩී වර්ධනය වේ. ඉතා ඉක්මණින් මුළු දත්ත සමුදායම මතකයට නංවනු ඇත. වඩා හොඳ සැලසුමක් LRUMap භාවිතා කරයි (මෑතකදී භාවිතා කළ වස්තු හැඹිලියේ තබා ගනී).
නිසැකවම, ඔබට දේවල් වඩාත් සංකීර්ණ කළ හැකිය:
බොහෝ විට සිදුවන්නේ කුමක්ද:
මෙම තොරතුරු වස්තුවට වෙනත් වස්තූන් වෙත යොමු කිරීම් තිබේ නම්, එය නැවත වෙනත් වස්තූන් වෙත යොමු කරයි. එක්තරා ආකාරයකින් ඔබට මෙය යම් ආකාරයක මතක කාන්දුවක් ලෙස සැලකිය හැකිය (නරක සැලසුම නිසා).
කිසිවෙකු අභ්යන්තර පන්තියේ උදාහරණ භාවිතා නොකිරීම සිත්ගන්නා සුළු යැයි මම සිතුවෙමි. ඔබට අභ්යන්තර පංතියක් තිබේ නම්; එය සහජයෙන්ම අඩංගු පන්තියට යොමු කිරීමක් පවත්වා ගනී. ඇත්ත වශයෙන්ම එය තාක්ෂණිකව මතක කාන්දුවක් නොවේ, මන්ද ජාවා අවසානයේ එය පිරිසිදු කරයි; නමුත් මෙය පංති බලාපොරොත්තු වූවාට වඩා දිගු කාලයක් රැඳී සිටීමට හේතු වේ.
public class Example1 {
public Example2 getNewExample2() {
return this.new Example2();
}
public class Example2 {
public Example2() {}
}
}
දැන් ඔබ නිදර්ශන 1 අමතා නිදර්ශන 1 ඉවතලමින් නිදර්ශන 2 ලබා ගන්නේ නම්, ඔබට සහජයෙන්ම නිදර්ශන 1 වස්තුවකට සබැඳියක් ඇත.
public class Referencer {
public static Example2 GetAnExample2() {
Example1 ex = new Example1();
return ex.getNewExample2();
}
public static void main(String[] args) {
Example2 ex = Referencer.GetAnExample2();
// As long as ex is reachable; Example1 will always remain in memory.
}
}
ඔබට නිශ්චිත කාලයකට වඩා වැඩි කාලයක් විචල්යයක් තිබේ නම් කටකතාවක් මා අසා ඇත; ජාවා උපකල්පනය කරන්නේ එය සැමවිටම පවතිනු ඇති අතර කේතයෙන් තවදුරටත් ළඟා විය නොහැකි නම් එය කිසි විටෙකත් පිරිසිදු කිරීමට උත්සාහ නොකරනු ඇති බවයි. නමුත් එය සම්පූර්ණයෙන්ම තහවුරු කර නොමැත.
Log4j මගින් යම් ආකාරයකින් මතකය කාන්දු වීමේ තත්වයක් මට මෑතකදී හමු විය.
Log4j හි මෙම යාන්ත්රණය Nested Diagnostic Context (NDC) ලෙස හැඳින්වේ ලෙස නම් කර ඇති අතර එය විවිධ ප්රභවයන්ගෙන් අන්තර් සම්බන්ධිත ලොග් ප්රතිදානය වෙන්කර හඳුනා ගැනීමේ මෙවලමකි. එන්ඩීසී වැඩ කරන කැටිති බව නූල් වේ, එබැවින් එය විවිධ නූල් වලින් ලොග් ප්රතිදානයන් වෙන වෙනම වෙන් කරයි.
නූල් විශේෂිත ටැග් ගබඩා කිරීම සඳහා, log4j හි NDC පන්තිය විසින් නූල් වස්තුව විසින්ම යතුරු ලියනය කරන ලද හැෂ් ටේබල් එකක් භාවිතා කරයි (නූල් හැඳුනුම්පත යැයි පැවසීමට වඩා වෙනස්ව), එබැවින් එන්ඩීසී ටැගය මතකයේ රැඳෙන තුරු නූල් වලින් එල්ලෙන සියලුම වස්තු වස්තුව ද මතකයේ රැඳේ. අපගේ වෙබ් යෙදුම තුළ, එක් ඉල්ලීමකින් ල logs ු-සටහන් වෙන් වෙන් වශයෙන් වෙන්කර හඳුනා ගැනීම සඳහා ඉල්ලීම් හැඳුනුම්පතක් සමඟ ලොග්අවුට් ටැග් කිරීමට අපි එන්ඩීසී භාවිතා කරමු. එන්ඩීසී ටැගය නූල් සමඟ සම්බන්ධ කරන කන්ටේනරය, ඉල්ලීමකින් ප්රතිචාරය ලබා දෙන අතරතුර එය ඉවත් කරයි. ඉල්ලීමක් සැකසීමේදී ළමා නූලක් බිහි වූ විට පහත සඳහන් කේතය වැනි දෙයක් ඇතිවිය.
pubclic class RequestProcessor {
private static final Logger logger = Logger.getLogger(RequestProcessor.class);
public void doSomething() {
....
final List<String> hugeList = new ArrayList<String>(10000);
new Thread() {
public void run() {
logger.info("Child thread spawned")
for(String s:hugeList) {
....
}
}
}.start();
}
}
එබැවින් එන්ඩීසී සන්දර්භයක් බිහි වූ පේළිගත නූල් සමඟ සම්බන්ධ විය. මෙම එන්ඩීසී සන්දර්භය සඳහා යතුර වූ නූල් වස්තුව, දැවැන්ත ලැයිස්තුගත වස්තුව එහි එල්ලී ඇති පේළිගත නූල් වේ. එම නිසා නූල් එය කරමින් සිටි දේ අවසන් කිරීමෙන් පසුව පවා, දැවැන්ත ලැයිස්තුව පිළිබඳ සඳහන එන්ඩීසී සන්දර්භය හේස්ටබල් විසින් සජීවීව තබා ඇති අතර එමඟින් මතක කාන්දු වීමක් සිදුවිය.
සම්මුඛ පරීක්ෂකවරයා බොහෝ විට පහත කේතය වැනි චක්රලේඛ යොමු කිරීමක් සොයමින් සිටියේය (අහම්බෙන් යොමු ගණනය කිරීම් භාවිතා කළ ඉතා පැරණි ජේවීඑම් වල මතකය පමණක් කාන්දු වන අතර එය තවදුරටත් එසේ නොවේ). නමුත් එය ඉතා අපැහැදිලි ප්රශ්නයකි, එබැවින් ජේවීඑම් මතක කළමනාකරණය පිළිබඳ ඔබේ අවබෝධය පෙන්වීමට මෙය ප්රධාන අවස්ථාවකි.
class A {
B bRef;
}
class B {
A aRef;
}
public class Main {
public static void main(String args[]) {
A myA = new A();
B myB = new B();
myA.bRef = myB;
myB.aRef = myA;
myA=null;
myB=null;
/* at this point, there is no access to the myA and myB objects, */
/* even though both objects still have active references. */
} /* main */
}
එවිට ඔබට යොමු ගණනය කිරීම් සමඟ ඉහත කේතය මතකය කාන්දු වන බව පැහැදිලි කළ හැකිය. නමුත් බොහෝ නූතන ජේවීඑම් යන්ත්ර තවදුරටත් යොමු ගණන් කිරීම භාවිතා නොකරයි, බොහෝ දෙනා ස්වීප් කසළ එකතු කරන්නෙකු භාවිතා කරයි, ඇත්ත වශයෙන්ම මෙම මතකය එකතු කරනු ඇත.
මීළඟට ඔබට මෙවැනි ස්වදේශීය සම්පතක් ඇති වස්තුවක් නිර්මාණය කිරීම පැහැදිලි කළ හැකිය:
public class Main {
public static void main(String args[]) {
Socket s = new Socket(InetAddress.getByName("google.com"),80);
s=null;
/* at this point, because you didn't close the socket properly, */
/* you have a leak of a native descriptor, which uses memory. */
}
}
එවිට ඔබට මෙය තාක්ෂණිකව මතක කාන්දුවක් බව පැහැදිලි කළ හැකිය, නමුත් ඇත්ත වශයෙන්ම කාන්දුව සිදුවන්නේ ජේවීඑම් හි ස්වදේශීය කේතය නිසා ඔබේ ජාවා කේතය මඟින් නිදහස් නොවූ යටින් පවතින දේශීය සම්පත් වෙන් කිරීමෙනි.
දවස අවසානයේදී, නවීන ජේවීඑම් සමඟ, ඔබ ජේවීඑම් දැනුවත් කිරීමේ සාමාන්ය විෂය පථයෙන් පිටත දේශීය සම්පතක් වෙන් කරන ජාවා කේත කිහිපයක් ලිවිය යුතුය.
ස්වදේශික කේත මාර්ගය සෑම කෙනෙකුටම සැමවිටම අමතක වේ. කාන්දු වීම සඳහා සරල සූත්රයක් මෙන්න:
malloc
. අමතන්න එපා free
.මතක තබා ගන්න, ස්වදේශීය කේතයේ මතක වෙන් කිරීම් පැමිණෙන්නේ ජේවීඑම් ගොඩවලිනි.
ස්ථිතික සිතියමක් සාදන්න සහ ඒ සඳහා දැඩි යොමු කිරීම් එකතු කරන්න. ඒවා කිසි විටෙකත් GC'd නොවනු ඇත.
public class Leaker {
private static final Map<String, Object> CACHE = new HashMap<String, Object>();
// Keep adding until failure.
public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}
එම පන්තියේ අවසාන ක්රමයේදී පන්තියක නව අවස්ථාවක් නිර්මාණය කිරීමෙන් ඔබට චලනය වන මතක කාන්දුවක් නිර්මාණය කළ හැකිය. අවසාන කාරකය අවස්ථා කිහිපයක් නිර්මාණය කරන්නේ නම් ප්රසාද ලකුණු. ඔබේ ගොඩවල ප්රමාණය අනුව තත්පර කිහිපයකින් සහ මිනිත්තු කිහිපයකින් මුළු ගොඩවල්ම කාන්දු වන සරල වැඩසටහනක් මෙන්න:
class Leakee {
public void check() {
if (depth > 2) {
Leaker.done();
}
}
private int depth;
public Leakee(int d) {
depth = d;
}
protected void finalize() {
new Leakee(depth + 1).check();
new Leakee(depth + 1).check();
}
}
public class Leaker {
private static boolean makeMore = true;
public static void done() {
makeMore = false;
}
public static void main(String[] args) throws InterruptedException {
// make a bunch of them until the garbage collector gets active
while (makeMore) {
new Leakee(0).check();
}
// sit back and watch the finalizers chew through memory
while (true) {
Thread.sleep(1000);
System.out.println("memory=" +
Runtime.getRuntime().freeMemory() + " / " +
Runtime.getRuntime().totalMemory());
}
}
}
කිසිවෙකු තවම මෙය පවසා ඇතැයි මම නොසිතමි: අවසාන () ක්රමය ඉක්මවා යාමෙන් ඔබට වස්තුවක් නැවත නැඟිටුවිය හැකිය. කසළ එකතු කරන්නා වස්තුව මත එක් වරක් පමණක් කැඳවනු ලබන අතර එමඟින් වස්තුව කිසි විටෙකත් විනාශ නොවනු ඇත.
finalize()
කැඳවනු නොලැබේ, නමුත් වැඩි යොමු කිරීම් නොමැති විට වස්තුව එකතු කරනු ලැබේ. කසළ එකතු කරන්නා 'කැඳවනු නොලැබේ'.
finalize()
ක්රමය හැඳින්විය හැක්කේ එක් වරක් පමණක් ජේවීඑම් ය, නමුත් මින් අදහස් කරන්නේ එම වස්තුව නැවත නැඟිටුවනු ලැබ නැවත කුණු කසළ එකතු කළ නොහැකි බවයි. finalize()
ක්රමයේ සම්පත් වැසීමේ කේතයක් තිබේ නම් මෙම කේතය නැවත ක්රියාත්මක නොවනු ඇත, මෙය මතක කාන්දු වීමට හේතු විය හැක.
මෑතකදී මට වඩාත් සියුම් ආකාරයේ සම්පත් කාන්දුවක් හමු විය. අපි පන්ති භාරකරුගේ getResourceAsStream හරහා සම්පත් විවෘත කරන අතර ආදාන ප්රවාහ හැසිරවීම් වසා නොතිබුණි.
අහ්, ඔයා කියන්න පුළුවන් මොන මෝඩයෙක්ද කියලා.
හොඳයි, මෙය සිත්ගන්නාසුලු දෙයක් වන්නේ: මේ ආකාරයෙන්, ඔබට ජේවීඑම් හි සංචයට වඩා යටින් පවතින ක්රියාවලියේ මතක ශක්තිය කාන්දු විය හැකිය.
ඔබට අවශ්ය වන්නේ ජාවා කේතයෙන් යොමු කෙරෙන ගොනුවක් සහිත භාජන ගොනුවක් පමණි. භාජන ගොනුව විශාල වන තරමට මතකය වෙන් කරනු ලැබේ.
පහත දැක්වෙන පන්තිය සමඟ ඔබට පහසුවෙන් එවැනි භාජනයක් නිර්මාණය කළ හැකිය:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class BigJarCreator {
public static void main(String[] args) throws IOException {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar")));
zos.putNextEntry(new ZipEntry("resource.txt"));
zos.write("not too much in here".getBytes());
zos.closeEntry();
zos.putNextEntry(new ZipEntry("largeFile.out"));
for (int i=0 ; i<10000000 ; i++) {
zos.write((int) (Math.round(Math.random()*100)+20));
}
zos.closeEntry();
zos.close();
}
}
BigJarCreator.java නම් ගොනුවකට අලවන්න, එය සම්පාදනය කර විධාන රේඛාවෙන් ක්රියාත්මක කරන්න:
javac BigJarCreator.java
java -cp . BigJarCreator
Et voilà: ඔබගේ වර්තමාන වැඩ කරන නාමාවලිය තුළ ලිපිගොනු දෙකක් ඇති භාජන ලේඛනාගාරයක් ඔබ සොයා ගනී.
දෙවන පන්තියක් නිර්මාණය කරමු:
public class MemLeak {
public static void main(String[] args) throws InterruptedException {
int ITERATIONS=100000;
for (int i=0 ; i<ITERATIONS ; i++) {
MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");
}
System.out.println("finished creation of streams, now waiting to be killed");
Thread.sleep(Long.MAX_VALUE);
}
}
මෙම පංතිය මූලික වශයෙන් කිසිවක් නොකරයි, නමුත් යොමු නොකළ InputStream වස්තු නිර්මාණය කරයි. එම වස්තූන් වහාම එකතු කරනු ලබන කසළ වන අතර එමඟින් ගොඩවල් ප්රමාණයට දායක නොවන්න. පවත්නා සම්පතක් භාජන ගොනුවකින් පැටවීම අපගේ උදාහරණයට වැදගත් වන අතර ප්රමාණය මෙහි වැදගත් වේ!
ඔබට සැකයක් ඇත්නම්, ඉහත පන්තිය සම්පාදනය කර ආරම්භ කිරීමට උත්සාහ කරන්න, නමුත් හොඳ ගොඩවල් ප්රමාණයක් (2 MB) තෝරා ගැනීමට වග බලා ගන්න:
javac MemLeak.java
java -Xmx2m -classpath .:big.jar MemLeak
ඔබට මෙහි OOM දෝෂයක් හමු නොවනු ඇත, කිසිදු සඳහනක් තබා නොමැති බැවින්, ඉහත උදාහරණයේ ඔබ කොතරම් විශාල ITERATIONS තෝරා ගත්තද යෙදුම දිගටම ක්රියාත්මක වේ. යෙදුම පොරොත්තු විධානය වෙත ළඟා නොවන්නේ නම් ඔබේ ක්රියාවලියේ මතක පරිභෝජනය (ඉහළින් (RES / RSS) හෝ ක්රියාවලි ගවේෂකය) වර්ධනය වේ. ඉහත සැකසුම තුළ, එය මතකයේ 150 MB පමණ වෙන් කරනු ඇත.
යෙදුම ආරක්ෂිතව ධාවනය කිරීමට ඔබට අවශ්ය නම්, ආදාන ප්රවාහය එය නිර්මාණය කළ ස්ථානයේම වසා දමන්න:
MemLeak.class.getClassLoader().getResourceAsStream("resource.txt").close();
ඔබේ ක්රියාවලිය පුනරාවර්තන ගණනට වඩා 35 MB නොඉක්මවනු ඇත.
ඉතා සරල හා පුදුම සහගතය.
බොහෝ අය යෝජනා කර ඇති පරිදි, සම්පත් කාන්දුවීම් ඇති කිරීම තරමක් පහසුය - ජේඩීබීසී උදාහරණ මෙන්. තථ්ය මතක කාන්දුවීම් ටිකක් අමාරුයි - විශේෂයෙන් ඔබ වෙනුවෙන් එය කිරීමට ජේවීඑම් හි බිඳුණු බිටු මත රඳා නොසිටින්නේ නම් ...
ඉතා විශාල අඩිපාරක් ඇති වස්තූන් නිර්මාණය කර පසුව ඒවාට ප්රවේශ වීමට නොහැකි වීම සැබෑ මතක කාන්දු නොවේ. කිසිවක් වෙත පිවිසිය නොහැකි නම් එය කසළ එකතු වනු ඇත, යම් දෙයකට එයට පිවිසිය හැකි නම් එය කාන්දුවක් නොවේ ...
කෙසේ වෙතත් වැඩ කිරීමට භාවිතා කළ එක් ක්රමයක් - සහ එය තවමත් පවතීදැයි මම නොදනිමි - ගැඹුරු තුනක වටකුරු දාමයක් තිබීම. වස්තුවෙහි A වස්තුවට යොමු දැක්වීමක් ඇති පරිදි, වස්තුව B ට වස්තුව C හා වස්තුව C වෙත වස්තුව A සඳහා යොමු දැක්වීමක් ඇත. ගැඹුරු දාම දෙකක් - A <--> B හි මෙන් දැන ගැනීමට GC දක්ෂ විය. - A සහ B වෙනත් කිසිවක් වෙත ප්රවේශ විය නොහැකි නමුත් ආරක්ෂිතව ත්රිවිධ දාමය හැසිරවිය නොහැකි නම් ආරක්ෂිතව එකතු කර ගත හැකිය ...
විශාල මතක කාන්දුවීම් ඇති කළ හැකි තවත් ක්රමයක් Map.Entry<K,V>
නම් aTreeMap
.
මෙය TreeMap
s ට පමණක් අදාළ වන්නේ මන්දැයි උපකල්පනය කිරීම දුෂ්කර ය, නමුත් ක්රියාත්මක කිරීම දෙස බැලීමෙන් හේතුව එය විය හැකිය: TreeMap.Entry
ගබඩාවක් එහි සහෝදර සහෝදරියන් ගැන සඳහන් කරයි, එබැවින් TreeMap
එකතු කිරීමට සුදානම් නම් , නමුත් වෙනත් පංතියක් ඕනෑම එකක් ගැන සඳහනක් දරයි එය Map.Entry
, එවිට මුළු සිතියම මතකයේ රැඳෙනු ඇත.
සැබෑ ජීවිතයේ තත්වය:
විශාල TreeMap
දත්ත ව්යුහයක් ලබා දෙන db විමසුමක් තිබීම ගැන සිතා බලන්න . TreeMap
මූලද්රව්ය ඇතුළත් කිරීමේ අනුපිළිවෙල රඳවා තබා ඇති බැවින් මිනිසුන් සාමාන්යයෙන් s භාවිතා කරයි .
public static Map<String, Integer> pseudoQueryDatabase();
විමසුම බොහෝ වාර ගණනක් කැඳවනු ලැබුවහොත් සහ එක් එක් විමසුම සඳහා (එසේ නම්, Map
ආපසු පැමිණි සෑම කෙනෙකුටම ) ඔබ Entry
කොතැනක හෝ ඉතිරි කළහොත්, මතකය නිරන්තරයෙන් වර්ධනය වේ.
පහත සඳහන් ආවරණ පන්තිය සලකා බලන්න:
class EntryHolder {
Map.Entry<String, Integer> entry;
EntryHolder(Map.Entry<String, Integer> entry) {
this.entry = entry;
}
}
අයදුම්පත:
public class LeakTest {
private final List<EntryHolder> holdersCache = new ArrayList<>();
private static final int MAP_SIZE = 100_000;
public void run() {
// create 500 entries each holding a reference to an Entry of a TreeMap
IntStream.range(0, 500).forEach(value -> {
// create map
final Map<String, Integer> map = pseudoQueryDatabase();
final int index = new Random().nextInt(MAP_SIZE);
// get random entry from map
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue().equals(index)) {
holdersCache.add(new EntryHolder(entry));
break;
}
}
// to observe behavior in visualvm
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
public static Map<String, Integer> pseudoQueryDatabase() {
final Map<String, Integer> map = new TreeMap<>();
IntStream.range(0, MAP_SIZE).forEach(i -> map.put(String.valueOf(i), i));
return map;
}
public static void main(String[] args) throws Exception {
new LeakTest().run();
}
}
සෑම pseudoQueryDatabase()
ඇමතුමකටම පසු , map
අවස්ථා එකතු කිරීමට සූදානම් විය යුතුය, නමුත් එය සිදු නොවේ, අවම වශයෙන් එකක් Entry
වෙනත් තැනක ගබඩා කර ඇති බැවින්.
ඔබගේ jvm
සැකසීම් මත පදනම්ව , යෙදුම මුල් අවධියේදී බිඳ වැටීමට ඉඩ ඇත OutOfMemoryError
.
visualvm
මතකය වර්ධනය වන ආකාරය මෙම ප්රස්ථාරයෙන් ඔබට දැක ගත හැකිය .
හැෂ් කළ දත්ත ව්යුහයක් ( HashMap
) සමඟ එය සිදු නොවේ .
A භාවිතා කරන විට මෙය ප්රස්ථාරයයි HashMap
.
විසඳුම? සුරැකීමට වඩා යතුර / අගය කෙලින්ම සුරකින්න (ඔබ දැනටමත් කර ඇති පරිදි) Map.Entry
.
නූල් අවසන් වන තුරු එකතු නොකෙරේ. ඒවා කසළ එකතු කිරීමේ මූලයන් ලෙස සේවය කරයි . ඒවා හුදෙක් ඒවා ගැන අමතක කිරීමෙන් හෝ ඒවා වෙත යොමු කිරීම් ඉවත් කිරීමෙන් නැවත ලබා ගත නොහැකි වස්තු කිහිපයෙන් එකකි.
සලකා බලන්න: සේවක නූල් අවසන් කිරීම සඳහා මූලික රටාව වන්නේ නූල් දකින යම් තත්ව විචල්යයක් සැකසීමයි. නූල් මඟින් වරින් වර විචල්යය පරීක්ෂා කර එය අවසන් කිරීමට සං signal ාවක් ලෙස භාවිතා කළ හැකිය. විචල්යය ප්රකාශයට පත් නොකළේ නම්, විචල්යයේ volatile
වෙනස නූල් මඟින් නොපෙනේ, එබැවින් එය අවසන් කිරීමට නොදැනේ. නැතහොත් සමහර නූල් වලට හවුල් වස්තුවක් යාවත්කාලීන කිරීමට අවශ්ය දැයි සිතන්න, නමුත් එය අගුළු දැමීමට උත්සාහ කරන අතරතුර අවහිර කරන්න.
ඔබ සතුව ඇත්තේ නූල් අතලොස්සක් නම්, මෙම දෝෂ බොහෝ විට පැහැදිලි වනු ඇත්තේ ඔබේ වැඩසටහන නිසි ලෙස ක්රියා කිරීම නවත්වන බැවිනි. ඔබට අවශ්ය පරිදි තවත් නූල් නිර්මාණය කරන නූල් තටාකයක් තිබේ නම්, යල්පැනගිය / සිරවී ඇති නූල් නොපෙනී යා හැකි අතර, එය දින නියමයක් නොමැතිව එකතු වී මතක කාන්දු වීමට හේතු වේ. නූල් ඔබේ යෙදුමේ වෙනත් දත්ත භාවිතා කිරීමට ඉඩ ඇත, එම නිසා ඔවුන් කෙලින්ම සඳහන් කරන කිසිවක් එකතු කිරීම වලක්වනු ඇත.
සෙල්ලම් උදාහරණයක් ලෙස:
static void leakMe(final Object object) {
new Thread() {
public void run() {
Object o = object;
for (;;) {
try {
sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {}
}
}
}.start();
}
System.gc()
ඔබ කැමති සියල්ල අමතන්න , නමුත් සම්මත කළ වස්තුව leakMe
කිසි විටෙකත් මිය යන්නේ නැත.
(* සංස්කරණය කරන ලදි *)
වලංගු නිදසුනක් වනුයේ නූල් සංචිත කර ඇති පරිසරයක ThreadLocal විචල්යයන් භාවිතා කිරීමයි.
නිදසුනක් ලෙස, වෙනත් වෙබ් සංරචක සමඟ සන්නිවේදනය කිරීම සඳහා සර්ව්ලෙට්ස් හි ThreadLocal විචල්යයන් භාවිතා කිරීම, කන්ටේනරය මඟින් නූල් සාදනු ලැබීම සහ අක්රීය ඒවා තටාකයක පවත්වා ගැනීම. ThreadLocal විචල්යයන්, නිවැරදිව පිරිසිදු නොකළ හොත්, එකම වෙබ් සංරචකය ඒවායේ අගයන් නැවත ලියන තුරු එහි ජීවත් වේ.
ඇත්ත වශයෙන්ම, හඳුනාගත් පසු, ගැටළුව පහසුවෙන් විසඳා ගත හැකිය.
සම්මුඛ පරීක්ෂක රවුම් යොමු විසඳුමක් සොයමින් සිටින්නට ඇත:
public static void main(String[] args) {
while (true) {
Element first = new Element();
first.next = new Element();
first.next.next = first;
}
}
මෙය කුණු එකතු කරන්නන් යොමු කිරීමේ සම්භාව්ය ගැටළුවකි. මෙම සීමාව නොමැති වඩාත් නවීන ඇල්ගොරිතමයක් ජේවීඑම් භාවිතා කරන බව ඔබ ආචාරශීලීව පැහැදිලි කරනු ඇත.
-වෙස් ටාර්ල්
first
ඒවා ප්රයෝජනවත් නොවන අතර කසළ එකතු කළ යුතුය. දී සඳහන් ඡන්ද ගණන් කිරීමේ කසළ එකතු, එය පිළිබඳ ක්රියාශීලී සඳහනක් (ම විසින්) ඇති නිසා වස්තුව freeed කළ නොහැකි වනු ඇත. කාන්දුව අවලංගු කිරීම සඳහා අනන්ත පුඩුවක් මෙහි ඇත: ඔබ වැඩසටහන ක්රියාත්මක කරන විට, මතකය දින නියමයක් නොමැතිව ඉහළ යනු ඇත.