sun.misc.GC 클래스

java core 2010. 5. 26. 14:40

sun.misc.GC 라는 클래스를 생전 처음 알게 되었다.

톰캣에서 sun.misc.GC를 이용하는 코드를 보게 되었다. 무엇인가 확인해본다.

최근에 톰캣 리더인 Mark Tomas 아저씨가 메모리릭 관련 리스너를 만든게 있다. 

JRE Memory Leak Prevention Listener (org.apache.catalina.core.JreMemoryLeakPreventionListener)

 http://tomcat.apache.org/tomcat-6.0-doc/config/listeners.html
gcDaemonProtection

Enables protection so that calls to sun.misc.GC.requestLatency(long) triggered by a web application do not result in a memory leak. Use of RMI is likely to trigger a call to this method. A side effect of enabling this protection is the creation of a thread named "GC Daemon". The protection is uses reflection to access internal Sun classes and may generate errors on startup on non-Sun JVMs. The default is true.


내용을 보면, Sun JVM 위에 돌아가는 GC Daemon를 하나 만들어 sun.misc.GC.requestLatency(long) 메소드를 날려주는 것이다. 쉽게 말해서 기존의 톰캣(자바)의 GC Daemon을 쓰지 않고 새로운 GC Daemon을 만드는 것이다.

http://www.docjar.com/docs/api/sun/misc/GC.html
public static LatencyRequest requestLatency(long latency) 
    Makes a new request for a garbage-collection latency of the given number of real-time milliseconds. A low-priority daemon thread makes a best effort to ensure that the maximum object-inspection age never exceeds the smallest of the currently active requests.


톰캣의 JreMemoryLeakPreventionListener.java 를 보면 의미를 더욱 더 명확히 할 수 있다.
톰캣 소스에서는 3600000 ms (즉, 3600초이며 60분의 latency를 주고 GC 하도록 메소드를 호출한다.

+            if (gcDaemonProtection) {
+                try {
+                    Class<?> clazz = Class.forName("sun.misc.GC");
+                    Method method = clazz.getDeclaredMethod("requestLatency",
+                            new Class[] {long.class});
+                    method.invoke(null, Long.valueOf(3600000));
+                } catch (ClassNotFoundException e) {
+                    if (System.getProperty("java.vendor").startsWith("Sun")) {
+                        log.error(sm.getString(
+                                "jreLeakListener.gcDaemonFail"), e);
+                    } else {
+                        log.debug(sm.getString(
+                                "jreLeakListener.gcDaemonFail"), e);
+                    }
+                }



내부적으로는 무슨 일을 하고 있을까? (하모니 소스는 이럴 때 보기 편하다..)


  167       /**  
168        * Represents an active garbage-collection latency request.  
Instances of  
169        * this class are created by the <code>{@link #requestLatency}</code> 
 170        * method.  Given a request, the only interesting operation is that of  
171        * cancellation.  172        */  
173       public static class LatencyRequest implements Comparable {  
174    
 175           /* Instance counter, used to generate unique identifers */  
176           private static long counter = 0;  
177     
178           /* Sorted set of active latency requests */  
179           private static SortedSet requests = null;  
180     
181           /* Examine the request set and reset the latency target if necessary.
  182            * Must be invoked while holding the lock.  
183            */  
184           private static void adjustLatencyIfNeeded() {  
185               if ((requests == null) || requests.isEmpty()) {  
186                   if (latencyTarget != NO_TARGET) {  
187                       setLatencyTarget(NO_TARGET);  
188                   }  
189               } else {  
190                   LatencyRequest r = (LatencyRequest)requests.first();  
191                   if (r.latency != latencyTarget) {  
192                       setLatencyTarget(r.latency);  
193                   } 
 194               }  
195           }  
196     
197           /* The requested latency, or NO_TARGET  
198            * if this request has been cancelled  
199            */  
200           private long latency;  
201     
202           /* Unique identifier for this request */  
203           private long id;  
204     
205           private LatencyRequest(long ms) {  
206               if (ms <= 0) { 
 207                   throw new IllegalArgumentException("Non-positive latency: "  
208                                                      + ms);  
209               }  
210               this.latency = ms;  
211               synchronized (lock) {  
212                   this.id = ++counter;  
213                   if (requests == null) {  
214                       requests = new TreeSet();  
215                   }  
216                   requests.add(this);  
217                   adjustLatencyIfNeeded();  
218               }  
219           }  
220     
221           /**  
222            * Cancels this latency request.  
223            *  
224            * @throws  IllegalStateException  
225            *          If this request has already been cancelled  
226            */  
227           public void cancel() {  
228               synchronized (lock) {  
229                   if (this.latency == NO_TARGET) { 
 230                       throw new IllegalStateException("Request already"  
231                                                       + " cancelled");  
232                   } 
 233                   if (!requests.remove(this)) {  
234                       throw new InternalError("Latency request "  
235                                               + this + " not found"); 
 236                   }  
237                   if (requests.isEmpty()) requests = null;  
238                   this.latency = NO_TARGET;  
239                   adjustLatencyIfNeeded();  
240               }  
241           } 
 242     
243           public int compareTo(Object o) { 
 244               LatencyRequest r = (LatencyRequest)o; 
 245               long d = this.latency - r.latency;  
246               if (d == 0) d = this.id - r.id;  
247               return (d < 0) ? -1 : ((d > 0) ? +1 : 0);  
248           }  
249     
250           public String toString() {  
251               return (LatencyRequest.class.getName()  
252                       + "[" + latency + "," + id + "]");  
253           } 
 254     
255       }  
256   

줄줄 따라가보니..Daemon을 생성하는 것이다. (Daemon있으면, notify 하면 되는 구조구요.)

Daemon은 Thread며, Daemon의 뜻에 맞게  low priority +1 를 두고 GC를 한다.
latency 시간의 값은 실제적으로는 expire되는 실제 wait값을 의미하는군..
   63       private static class LatencyLock extends Object { };   
64       private static Object lock = new LatencyLock();
private static class Daemon extends Thread {
   87      
88           public void run() {   
89               for (;;) {  
 90                   long l;   
91                   synchronized (lock) {   
92      
93                       l = latencyTarget;   
94                       if (l == NO_TARGET) {   
95                           /* No latency target, so exit */   
96                           GC.daemon = null;   
97                           return;   98                       }   
99     
100                       long d = maxObjectInspectionAge();  
101                       if (d >= l) {  
102                        /* Do a full collection.  There is a remote possibility
103                        * that a full collection will occurr between the time
 104                       * we sample the inspection age and the time the GC  
105                        * actually starts, but this is sufficiently unlikely  
106                         * that it doesn't seem worth the more expensive JVM 
 107                      * interface that would be required.  
108                            */  
109                           System.gc();  
110                           d = 0;  
111                       }  
112     
113                       /* Wait for the latency period to expire, 
 114                        * or for notification that the period has changed  
115                        */  
116                       try {  
117                           lock.wait(l - d);  
118                       } catch (InterruptedException x) {  
119                           continue;  
120                       }  
121                   }  
122               }  
123           }  
124     
125           private Daemon(ThreadGroup tg) {  
126               super(tg, "GC Daemon");  
127           }  
128    
 129           /* Create a new daemon thread in the root thread group */  
130           public static void create() { 
 131               PrivilegedAction pa = new PrivilegedAction() { 
 132                   public Object run() {  
133                       ThreadGroup tg = Thread.currentThread().getThreadGroup();  
134                       for (ThreadGroup tgn = tg; 
 135                            tgn != null;  
136                            tg = tgn, tgn = tg.getParent());  
137                       Daemon d = new Daemon(tg);  
138                       d.setDaemon(true);  
139                       d.setPriority(Thread.MIN_PRIORITY + 1); 
 140                       d.start();  
141                       GC.daemon = d;  
142                       return null;  
143                   }};  
144               AccessController.doPrivileged(pa);  
145           }  
146     
147       }


ObjectInspectAge가 무엇인가 API를 찾아봤더니. 다음과 같이 나왔다.
GC에 의해서 마지막으로 inspected된 heap object를 기준으로 얼마나 시간이 지났는지를 알려주는 최대값을 의미한다.. 내 생각에는 gc 하고 나서 얼마나 시간이 지났는지를 알려준다. 그 값을 염두하고 GC를 호출하는 저 Sense!!! 굿! 
    Returns the maximum object-inspection age, which is the number of real-time milliseconds that have elapsed since the least-recently-inspected heap object was last inspected by the garbage collector.


참고로 소스를 찾아보았다.. (apache harmony가 이럴 때 좋군.. 공부 대상 목표로 삼아두고..

Source : http://www.docjar.com/html/api/sun/misc/GC.java.html

다음의 결과는 무엇이 나올까.
System.out.println(sun.misc.GC.currentLatencyTarget());
System.out.println(sun.misc.GC.maxObjectInspectionAge());


0
1274847968291


 API 내용을 보면 좋을 것 같아서  올려놔 본다.
public static long currentLatencyTarget() 
    Returns the current smallest garbage-collection latency request, or zero if there are no active requests.

      Returns the maximum object-inspection age, which is the number of real-time milliseconds that have elapsed since the least-recently-inspected heap object was last inspected by the garbage collector.

      For simple stop-the-world collectors this value is just the time since the most recent collection. For generational collectors it is the time since the oldest generation was most recently collected. Other collectors are free to return a pessimistic estimate of the elapsed time, or simply the time since the last full collection was performed.

      Note that in the presence of reference objects, a given object that is no longer strongly reachable may have to be inspected multiple times before it can be reclaimed.


Posted by '김용환'
,