1 /*
2  * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.tools.jconsole;
27 
28 import java.awt.*;
29 import java.awt.event.*;
30 import java.io.*;
31 import java.lang.management.*;
32 import java.lang.reflect.*;
33 import java.util.*;
34 import java.util.concurrent.*;
35 
36 import javax.accessibility.*;
37 import javax.management.*;
38 import javax.management.openmbean.CompositeData;
39 import javax.swing.*;
40 import javax.swing.border.*;
41 
42 
43 import static sun.tools.jconsole.Formatter.*;
44 import static sun.tools.jconsole.Utilities.*;
45 
46 @SuppressWarnings("serial")
47 class MemoryTab extends Tab implements ActionListener, ItemListener {
48     JComboBox<Plotter> plotterChoice;
49     TimeComboBox timeComboBox;
50     JButton gcButton;
51 
52     PlotterPanel plotterPanel;
53     JPanel bottomPanel;
54     HTMLPane details;
55     PoolChart poolChart;
56 
57     ArrayList<Plotter> plotterList;
58     Plotter heapPlotter, nonHeapPlotter;
59 
60     private MemoryOverviewPanel overviewPanel;
61 
62     private static final String usedKey        = "used";
63     private static final String committedKey   = "committed";
64     private static final String maxKey         = "max";
65     private static final String thresholdKey   = "threshold";
66     private static final Color  usedColor      = Plotter.defaultColor;
67     private static final Color  committedColor = null;
68     private static final Color  maxColor       = null;
69     private static final Color  thresholdColor = Color.red;
70 
71     /*
72       Hierarchy of panels and layouts for this tab:
73 
74         MemoryTab (BorderLayout)
75 
76             North:  topPanel (BorderLayout)
77 
78                         Center: controlPanel (FlowLayout)
79                                     plotterChoice, timeComboBox
80 
81                         East:   topRightPanel (FlowLayout)
82                                     gcButton
83 
84             Center: plotterPanel
85 
86                         Center: plotter
87 
88             South:  bottomPanel (BorderLayout)
89 
90                         Center: details
91                         East:   poolChart
92     */
93 
94 
getTabName()95     public static String getTabName() {
96         return Messages.MEMORY;
97     }
98 
MemoryTab(VMPanel vmPanel)99     public MemoryTab(VMPanel vmPanel) {
100         super(vmPanel, getTabName());
101 
102         setLayout(new BorderLayout(0, 0));
103         setBorder(new EmptyBorder(4, 4, 3, 4));
104 
105         JPanel topPanel     = new JPanel(new BorderLayout());
106                plotterPanel = new PlotterPanel(null);
107                bottomPanel  = new JPanel(new BorderLayout());
108 
109         add(topPanel,     BorderLayout.NORTH);
110         add(plotterPanel, BorderLayout.CENTER);
111 
112         JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 20, 5));
113         topPanel.add(controlPanel, BorderLayout.CENTER);
114 
115         // Plotter choice
116         plotterChoice = new JComboBox<Plotter>();
117         plotterChoice.addItemListener(this);
118         controlPanel.add(new LabeledComponent(Messages.CHART_COLON,
119                                               Resources.getMnemonicInt(Messages.CHART_COLON),
120                                               plotterChoice));
121 
122         // Range control
123         timeComboBox = new TimeComboBox();
124         controlPanel.add(new LabeledComponent(Messages.TIME_RANGE_COLON,
125                                               Resources.getMnemonicInt(Messages.TIME_RANGE_COLON),
126                                               timeComboBox));
127 
128         gcButton = new JButton(Messages.PERFORM_GC);
129         gcButton.setMnemonic(Resources.getMnemonicInt(Messages.PERFORM_GC));
130         gcButton.addActionListener(this);
131         gcButton.setToolTipText(Messages.PERFORM_GC_TOOLTIP);
132         JPanel topRightPanel = new JPanel();
133         topRightPanel.setBorder(new EmptyBorder(0, 65-8, 0, 70));
134         topRightPanel.add(gcButton);
135         topPanel.add(topRightPanel, BorderLayout.AFTER_LINE_ENDS);
136 
137         bottomPanel.setBorder(new CompoundBorder(new TitledBorder(Messages.DETAILS),
138                                                   new EmptyBorder(10, 10, 10, 10)));
139 
140         details = new HTMLPane();
141         setAccessibleName(details, Messages.DETAILS);
142         bottomPanel.add(new JScrollPane(details), BorderLayout.CENTER);
143 
144         poolChart = new PoolChart();
145         bottomPanel.add(poolChart, BorderLayout.AFTER_LINE_ENDS);
146     }
147 
148 
createPlotters()149     private void createPlotters() throws IOException {
150         plotterList = new ArrayList<Plotter>();
151 
152         ProxyClient proxyClient = vmPanel.getProxyClient();
153 
154         heapPlotter = new Plotter(Plotter.Unit.BYTES) {
155             public String toString() {
156                 return Messages.HEAP_MEMORY_USAGE;
157             }
158         };
159         proxyClient.addWeakPropertyChangeListener(heapPlotter);
160 
161         nonHeapPlotter = new Plotter(Plotter.Unit.BYTES) {
162             public String toString() {
163                 return Messages.NON_HEAP_MEMORY_USAGE;
164             }
165         };
166 
167         setAccessibleName(heapPlotter,
168                           Messages.MEMORY_TAB_HEAP_PLOTTER_ACCESSIBLE_NAME);
169         setAccessibleName(nonHeapPlotter,
170                           Messages.MEMORY_TAB_NON_HEAP_PLOTTER_ACCESSIBLE_NAME);
171 
172         proxyClient.addWeakPropertyChangeListener(nonHeapPlotter);
173 
174         heapPlotter.createSequence(usedKey,         Messages.USED,      usedColor,      true);
175         heapPlotter.createSequence(committedKey,    Messages.COMMITTED, committedColor, false);
176         heapPlotter.createSequence(maxKey,          Messages.MAX,       maxColor,       false);
177 
178         nonHeapPlotter.createSequence(usedKey,      Messages.USED,      usedColor,      true);
179         nonHeapPlotter.createSequence(committedKey, Messages.COMMITTED, committedColor, false);
180         nonHeapPlotter.createSequence(maxKey,       Messages.MAX,       maxColor,       false);
181 
182         plotterList.add(heapPlotter);
183         plotterList.add(nonHeapPlotter);
184 
185         // Now add memory pools
186         Map<ObjectName, MBeanInfo> mBeanMap = proxyClient.getMBeans("java.lang");
187         Set<ObjectName> keys = mBeanMap.keySet();
188         ObjectName[] objectNames = keys.toArray(new ObjectName[keys.size()]);
189         ArrayList<PoolPlotter> nonHeapPlotters = new ArrayList<PoolPlotter>(2);
190         for (ObjectName objectName : objectNames) {
191             String type = objectName.getKeyProperty("type");
192             if (type.equals("MemoryPool")) {
193                 String name = Resources.format(Messages.MEMORY_POOL_LABEL,
194                                                objectName.getKeyProperty("name"));
195                 // Heap or non-heap?
196                 boolean isHeap = false;
197                 AttributeList al =
198                     proxyClient.getAttributes(objectName,
199                                               new String[] { "Type" });
200                 if (al.size() > 0) {
201                     isHeap = MemoryType.HEAP.name().equals(((Attribute)al.get(0)).getValue());
202                 }
203                 PoolPlotter poolPlotter = new PoolPlotter(objectName, name, isHeap);
204                 proxyClient.addWeakPropertyChangeListener(poolPlotter);
205 
206                 poolPlotter.createSequence(usedKey,      Messages.USED,      usedColor,      true);
207                 poolPlotter.createSequence(committedKey, Messages.COMMITTED, committedColor, false);
208                 poolPlotter.createSequence(maxKey,       Messages.MAX,       maxColor,       false);
209                 poolPlotter.createSequence(thresholdKey, Messages.THRESHOLD, thresholdColor, false);
210                 poolPlotter.setUseDashedTransitions(thresholdKey, true);
211 
212                 if (isHeap) {
213                     plotterList.add(poolPlotter);
214                 } else {
215                     // Will be added to plotterList below
216                     nonHeapPlotters.add(poolPlotter);
217                 }
218             }
219         }
220         // Add non-heap plotters last
221         for (PoolPlotter poolPlotter : nonHeapPlotters) {
222             plotterList.add(poolPlotter);
223         }
224     }
225 
226 
itemStateChanged(ItemEvent ev)227     public void itemStateChanged(ItemEvent ev) {
228         if (ev.getStateChange() == ItemEvent.SELECTED) {
229             Plotter plotter = (Plotter)plotterChoice.getSelectedItem();
230             plotterPanel.setPlotter(plotter);
231             plotterPanel.repaint();
232         }
233     }
234 
gc()235     public void gc() {
236         new Thread("MemoryPanel.gc") {
237             public void run() {
238                 ProxyClient proxyClient = vmPanel.getProxyClient();
239                 try {
240                     proxyClient.getMemoryMXBean().gc();
241                 } catch (UndeclaredThrowableException e) {
242                     proxyClient.markAsDead();
243                 } catch (IOException e) {
244                     // Ignore
245                 }
246             }
247         }.start();
248     }
249 
newSwingWorker()250     public SwingWorker<?, ?> newSwingWorker() {
251         return new SwingWorker<Boolean, Object>() {
252             private long[] used, committed, max, threshold;
253             private long timeStamp;
254             private String detailsStr;
255             private boolean initialRun = false;
256 
257             public Boolean doInBackground() {
258                 ProxyClient proxyClient = vmPanel.getProxyClient();
259 
260                 if (plotterList == null) {
261                     try {
262                         createPlotters();
263                     } catch (UndeclaredThrowableException e) {
264                         proxyClient.markAsDead();
265                         return false;
266                     } catch (final IOException ex) {
267                         return false;
268                     }
269                     initialRun = true;
270                 }
271 
272                 int n = plotterList.size();
273                 used      = new long[n];
274                 committed = new long[n];
275                 max       = new long[n];
276                 threshold = new long[n];
277                 timeStamp = System.currentTimeMillis();
278 
279                 for (int i = 0; i < n; i++) {
280                     Plotter plotter = plotterList.get(i);
281                     MemoryUsage mu = null;
282                     used[i] = -1L;
283                     threshold[i] = -1L;
284 
285                     try {
286                         if (plotter instanceof PoolPlotter) {
287                             PoolPlotter poolPlotter = (PoolPlotter)plotter;
288                             ObjectName objectName = poolPlotter.objectName;
289                             AttributeList al =
290                                 proxyClient.getAttributes(objectName,
291                                                           new String[] { "Usage", "UsageThreshold" });
292                             if (al.size() > 0) {
293                                 CompositeData cd = (CompositeData)((Attribute)al.get(0)).getValue();
294                                 mu = MemoryUsage.from(cd);
295 
296                                 if (al.size() > 1) {
297                                     threshold[i] = (Long)((Attribute)al.get(1)).getValue();
298                                 }
299                             }
300                         } else if (plotter == heapPlotter) {
301                             mu = proxyClient.getMemoryMXBean().getHeapMemoryUsage();
302                         } else if (plotter == nonHeapPlotter) {
303                             mu = proxyClient.getMemoryMXBean().getNonHeapMemoryUsage();
304                         }
305                     } catch (UndeclaredThrowableException e) {
306                         proxyClient.markAsDead();
307                         return false;
308                     } catch (IOException ex) {
309                         // Skip this plotter
310                     }
311 
312                     if (mu != null) {
313                         used[i]      = mu.getUsed();
314                         committed[i] = mu.getCommitted();
315                         max[i]       = mu.getMax();
316                     }
317                 }
318                 detailsStr = formatDetails();
319 
320                 return true;
321             }
322 
323             protected void done() {
324                 try {
325                     if (!get()) {
326                         return;
327                     }
328                 } catch (InterruptedException ex) {
329                     return;
330                 } catch (ExecutionException ex) {
331                     if (JConsole.isDebug()) {
332                         ex.printStackTrace();
333                     }
334                     return;
335                 }
336 
337                 if (initialRun) {
338                     // Add Memory Pools
339                     for (Plotter p : plotterList) {
340                         plotterChoice.addItem(p);
341                         timeComboBox.addPlotter(p);
342                     }
343                     add(bottomPanel,  BorderLayout.SOUTH);
344                 }
345 
346 
347                 int n = plotterList.size();
348                 int poolCount = 0;
349 
350                 for (int i = 0; i < n; i++) {
351                     Plotter plotter = plotterList.get(i);
352                     if (used[i] >= 0L) {
353                         if (plotter instanceof PoolPlotter) {
354                             plotter.addValues(timeStamp, used[i], committed[i], max[i], threshold[i]);
355                             if (threshold[i] > 0L) {
356                                 plotter.setIsPlotted(thresholdKey, true);
357                             }
358                             poolChart.setValue(poolCount++, (PoolPlotter)plotter,
359                                                used[i], threshold[i], max[i]);
360                         } else {
361                             plotter.addValues(timeStamp, used[i], committed[i], max[i]);
362                         }
363 
364                         if (plotter == heapPlotter && overviewPanel != null) {
365                             overviewPanel.getPlotter().addValues(timeStamp, used[i]);
366                             overviewPanel.updateMemoryInfo(used[i], committed[i], max[i]);
367                         }
368                     }
369                 }
370                 details.setText(detailsStr);
371             }
372         };
373     }
374 
formatDetails()375     private String formatDetails() {
376         ProxyClient proxyClient = vmPanel.getProxyClient();
377         if (proxyClient.isDead()) {
378             return "";
379         }
380 
381         String text = "<table cellspacing=0 cellpadding=0>";
382 
383         Plotter plotter = (Plotter)plotterChoice.getSelectedItem();
384         if (plotter == null) {
385             return "";
386         }
387 
388         //long time = plotter.getLastTimeStamp();
389         long time = System.currentTimeMillis();
390         String timeStamp = formatDateTime(time);
391         text += newRow(Messages.TIME, timeStamp);
392 
393         long used = plotter.getLastValue(usedKey);
394         long committed = plotter.getLastValue(committedKey);
395         long max = plotter.getLastValue(maxKey);
396         long threshold = plotter.getLastValue(thresholdKey);
397 
398         text += newRow(Messages.USED, formatKBytes(used));
399         if (committed > 0L) {
400             text += newRow(Messages.COMMITTED, formatKBytes(committed));
401         }
402         if (max > 0L) {
403             text += newRow(Messages.MAX, formatKBytes(max));
404         }
405         if (threshold > 0L) {
406             text += newRow(Messages.USAGE_THRESHOLD, formatKBytes(threshold));
407         }
408 
409         try {
410             Collection<GarbageCollectorMXBean> garbageCollectors =
411                 proxyClient.getGarbageCollectorMXBeans();
412 
413             boolean descPrinted = false;
414             for (GarbageCollectorMXBean garbageCollectorMBean : garbageCollectors) {
415                 String gcName = garbageCollectorMBean.getName();
416                 long gcCount = garbageCollectorMBean.getCollectionCount();
417                 long gcTime = garbageCollectorMBean.getCollectionTime();
418                 String str = Resources.format(Messages.GC_TIME_DETAILS, justify(formatTime(gcTime), 14),
419                                               gcName,
420                                               String.format("%,d",gcCount));
421                 if (!descPrinted) {
422                     text += newRow(Messages.GC_TIME, str);
423                     descPrinted = true;
424                 } else {
425                     text += newRow(null, str);
426                 }
427            }
428         } catch (IOException e) {
429         }
430 
431         return text;
432     }
433 
actionPerformed(ActionEvent ev)434     public void actionPerformed(ActionEvent ev) {
435         Object src = ev.getSource();
436         if (src == gcButton) {
437             gc();
438         }
439     }
440 
441     private class PoolPlotter extends Plotter {
442         ObjectName objectName;
443         String name;
444         boolean isHeap;
445         long value, threshold, max;
446         int barX;
447 
PoolPlotter(ObjectName objectName, String name, boolean isHeap)448         public PoolPlotter(ObjectName objectName, String name, boolean isHeap) {
449             super(Plotter.Unit.BYTES);
450 
451             this.objectName = objectName;
452             this.name       = name;
453             this.isHeap     = isHeap;
454 
455             setAccessibleName(this,
456                               Resources.format(Messages.MEMORY_TAB_POOL_PLOTTER_ACCESSIBLE_NAME,
457                                                name));
458         }
459 
460 
toString()461         public String toString() {
462             return name;
463         }
464     }
465 
466     private class PoolChart extends BorderedComponent
467                             implements Accessible, MouseListener {
468         final int height       = 150;
469         final int leftMargin   =  50;
470         final int rightMargin  =  23;
471         final int bottomMargin =  35;
472         final int barWidth     =  22;
473         final int barGap       =   3;
474         final int groupGap     =   8;
475         final int barHeight    = height * 2 / 3;
476 
477         final Color greenBar           = new Color(100, 255, 100);
478         final Color greenBarBackground = new Color(210, 255, 210);
479         final Color redBarBackground   = new Color(255, 210, 210);
480 
481         Font smallFont = null;
482 
483         ArrayList<PoolPlotter> poolPlotters = new ArrayList<PoolPlotter>(5);
484 
485         int nHeapPools    = 0;
486         int nNonHeapPools = 0;
487         Rectangle heapRect    = new Rectangle(leftMargin,            height - bottomMargin + 6, barWidth, 20);
488         Rectangle nonHeapRect = new Rectangle(leftMargin + groupGap, height - bottomMargin + 6, barWidth, 20);
489 
PoolChart()490         public PoolChart() {
491             super(null, null);
492 
493             setFocusable(true);
494             addMouseListener(this);
495             ToolTipManager.sharedInstance().registerComponent(this);
496         }
497 
setValue(int poolIndex, PoolPlotter poolPlotter, long value, long threshold, long max)498         public void setValue(int poolIndex, PoolPlotter poolPlotter,
499                              long value, long threshold, long max) {
500             poolPlotter.value = value;
501             poolPlotter.threshold = threshold;
502             poolPlotter.max = max;
503 
504             if (poolIndex == poolPlotters.size()) {
505                 poolPlotters.add(poolPlotter);
506                 if (poolPlotter.isHeap) {
507                     poolPlotter.barX = nHeapPools * (barWidth + barGap);
508                     nHeapPools++;
509                     heapRect.width = nHeapPools * barWidth + (nHeapPools - 1) * barGap;
510                     nonHeapRect.x  = leftMargin + heapRect.width + groupGap;
511                 } else {
512                     poolPlotter.barX = nonHeapRect.x - leftMargin + nNonHeapPools * (barWidth + barGap);
513                     nNonHeapPools++;
514                     nonHeapRect.width = nNonHeapPools * barWidth + (nNonHeapPools - 1) * barGap;
515                 }
516             } else {
517                 poolPlotters.set(poolIndex, poolPlotter);
518             }
519             repaint();
520         }
521 
paintPoolBar(Graphics g, PoolPlotter poolPlotter)522         private void paintPoolBar(Graphics g, PoolPlotter poolPlotter) {
523             Rectangle barRect = getBarRect(poolPlotter);
524             g.setColor(Color.gray);
525             g.drawRect(barRect.x, barRect.y, barRect.width, barRect.height);
526 
527             long value = poolPlotter.value;
528             long max   = poolPlotter.max;
529             if (max > 0L) {
530                 g.translate(barRect.x, barRect.y);
531 
532                 // Paint green background
533                 g.setColor(greenBarBackground);
534                 g.fillRect(1, 1, barRect.width - 1, barRect.height - 1);
535 
536                 int greenHeight = (int)(value * barRect.height / max);
537                 long threshold = poolPlotter.threshold;
538                 if (threshold > 0L) {
539                     int redHeight = (int)(threshold * barRect.height / max);
540 
541                     // Paint red background
542                     g.setColor(redBarBackground);
543                     g.fillRect(1, 1, barRect.width - 1, barRect.height - redHeight);
544 
545                     if (value > threshold) {
546                         // Over threshold, paint red bar
547                         g.setColor(thresholdColor);
548                         g.fillRect(1, barRect.height - greenHeight,
549                                    barRect.width - 1, greenHeight - redHeight);
550                         greenHeight = redHeight;
551                     }
552                 }
553 
554                 // Paint green bar
555                 g.setColor(greenBar);
556                 g.fillRect(1, barRect.height - greenHeight,
557                            barRect.width - 1, greenHeight);
558 
559                 g.translate(-barRect.x, -barRect.y);
560             }
561         }
562 
paintComponent(Graphics g)563         public void paintComponent(Graphics g) {
564             super.paintComponent(g);
565 
566             if (poolPlotters.size() == 0) {
567                 return;
568             }
569 
570             if (smallFont == null) {
571                 smallFont = g.getFont().deriveFont(9.0F);
572             }
573 
574             // Paint background for chart area
575             g.setColor(getBackground());
576             Rectangle r = g.getClipBounds();
577             g.fillRect(r.x, r.y, r.width, r.height);
578 
579             g.setFont(smallFont);
580             FontMetrics fm = g.getFontMetrics();
581             int fontDescent = fm.getDescent();
582 
583             // Paint percentage axis
584             g.setColor(getForeground());
585             for (int pc : new int[] { 0, 25, 50, 75, 100 }) {
586                 String str = pc + "% --";
587                 g.drawString(str,
588                              leftMargin - fm.stringWidth(str) - 4,
589                              height - bottomMargin - (pc * barHeight / 100) + fontDescent + 1);
590             }
591 
592             for (PoolPlotter poolPlotter : poolPlotters) {
593                 paintPoolBar(g, poolPlotter);
594             }
595 
596             g.setColor(Color.gray);
597             g.drawRect(heapRect.x,    heapRect.y,    heapRect.width,    heapRect.height);
598             g.drawRect(nonHeapRect.x, nonHeapRect.y, nonHeapRect.width, nonHeapRect.height);
599 
600             Color heapColor    = greenBar;
601             Color nonHeapColor = greenBar;
602 
603 
604             for (PoolPlotter poolPlotter : poolPlotters) {
605                 if (poolPlotter.threshold > 0L && poolPlotter.value > poolPlotter.threshold) {
606                     if (poolPlotter.isHeap) {
607                         heapColor = thresholdColor;
608                     } else {
609                         nonHeapColor = thresholdColor;
610                     }
611                 }
612             }
613             g.setColor(heapColor);
614             g.fillRect(heapRect.x + 1,    heapRect.y + 1,    heapRect.width - 1,    heapRect.height - 1);
615             g.setColor(nonHeapColor);
616             g.fillRect(nonHeapRect.x + 1, nonHeapRect.y + 1, nonHeapRect.width - 1, nonHeapRect.height - 1);
617 
618             String str = Messages.HEAP;
619             int stringWidth = fm.stringWidth(str);
620             int x = heapRect.x + (heapRect.width - stringWidth) / 2;
621             int y = heapRect.y + heapRect.height - 6;
622             g.setColor(Color.white);
623             g.drawString(str, x-1, y-1);
624             g.drawString(str, x+1, y-1);
625             g.drawString(str, x-1, y+1);
626             g.drawString(str, x+1, y+1);
627             g.setColor(Color.black);
628             g.drawString(str, x, y);
629 
630             str = Messages.NON_HEAP;
631             stringWidth = fm.stringWidth(str);
632             x = nonHeapRect.x + (nonHeapRect.width - stringWidth) / 2;
633             y = nonHeapRect.y + nonHeapRect.height - 6;
634             g.setColor(Color.white);
635             g.drawString(str, x-1, y-1);
636             g.drawString(str, x+1, y-1);
637             g.drawString(str, x-1, y+1);
638             g.drawString(str, x+1, y+1);
639             g.setColor(Color.black);
640             g.drawString(str, x, y);
641 
642             // Highlight current plotter
643             g.setColor(Color.blue);
644             r = null;
645             Plotter plotter = (Plotter)plotterChoice.getSelectedItem();
646             if (plotter == heapPlotter) {
647                 r = heapRect;
648             } else if (plotter == nonHeapPlotter) {
649                 r = nonHeapRect;
650             } else if (plotter instanceof PoolPlotter) {
651                 r = getBarRect((PoolPlotter)plotter);
652             }
653             if (r != null) {
654                 g.drawRect(r.x - 1, r.y - 1, r.width + 2, r.height + 2);
655             }
656         }
657 
getBarRect(PoolPlotter poolPlotter)658         private Rectangle getBarRect(PoolPlotter poolPlotter) {
659             return new Rectangle(leftMargin + poolPlotter.barX,
660                                  height - bottomMargin - barHeight,
661                                  barWidth, barHeight);
662         }
663 
getPreferredSize()664         public Dimension getPreferredSize() {
665             return new Dimension(nonHeapRect.x + nonHeapRect.width + rightMargin,
666                                  height);
667         }
668 
mouseClicked(MouseEvent e)669         public void mouseClicked(MouseEvent e) {
670             requestFocusInWindow();
671             Plotter plotter = getPlotter(e);
672 
673             if (plotter != null && plotter != plotterChoice.getSelectedItem()) {
674                 plotterChoice.setSelectedItem(plotter);
675                 repaint();
676             }
677         }
678 
getToolTipText(MouseEvent e)679         public String getToolTipText(MouseEvent e) {
680             Plotter plotter = getPlotter(e);
681 
682             return (plotter != null) ? plotter.toString() : null;
683         }
684 
getPlotter(MouseEvent e)685         private Plotter getPlotter(MouseEvent e) {
686             Point p = e.getPoint();
687             Plotter plotter = null;
688 
689             if (heapRect.contains(p)) {
690                 plotter = heapPlotter;
691             } else if (nonHeapRect.contains(p)) {
692                 plotter = nonHeapPlotter;
693             } else {
694                 for (PoolPlotter poolPlotter : poolPlotters) {
695                     if (getBarRect(poolPlotter).contains(p)) {
696                         plotter = poolPlotter;
697                         break;
698                     }
699                 }
700             }
701             return plotter;
702         }
703 
mousePressed(MouseEvent e)704         public void mousePressed(MouseEvent e) {}
mouseReleased(MouseEvent e)705         public void mouseReleased(MouseEvent e) {}
mouseEntered(MouseEvent e)706         public void mouseEntered(MouseEvent e) {}
mouseExited(MouseEvent e)707         public void mouseExited(MouseEvent e) {}
708 
709 
getAccessibleContext()710         public AccessibleContext getAccessibleContext() {
711             if (accessibleContext == null) {
712                 accessibleContext = new AccessiblePoolChart();
713             }
714             return accessibleContext;
715         }
716 
717         protected class AccessiblePoolChart extends AccessibleJPanel {
getAccessibleName()718             public String getAccessibleName() {
719                 String name = Messages.MEMORY_TAB_POOL_CHART_ACCESSIBLE_NAME;
720 
721                 String keyValueList = "";
722                 for (PoolPlotter poolPlotter : poolPlotters) {
723                     String value = (poolPlotter.value * 100 / poolPlotter.max) + "%";
724                     // Assume format string ends with newline
725                     keyValueList +=
726                         Resources.format(Messages.PLOTTER_ACCESSIBLE_NAME_KEY_AND_VALUE,
727                                          poolPlotter.toString(), value);
728                     if (poolPlotter.threshold > 0L) {
729                         String threshold =
730                             (poolPlotter.threshold * 100 / poolPlotter.max) + "%";
731                         if (poolPlotter.value > poolPlotter.threshold) {
732                             keyValueList +=
733                                Resources.format(Messages.MEMORY_TAB_POOL_CHART_ABOVE_THRESHOLD,
734                                                 threshold);
735                         } else {
736                             keyValueList +=
737                                     Resources.format(Messages.MEMORY_TAB_POOL_CHART_BELOW_THRESHOLD,
738                                                      threshold);
739                         }
740                     }
741                 }
742 
743                 return name + "\n" + keyValueList + ".";
744             }
745         }
746     }
747 
748 
getOverviewPanels()749     OverviewPanel[] getOverviewPanels() {
750         if (overviewPanel == null) {
751             overviewPanel = new MemoryOverviewPanel();
752         }
753         return new OverviewPanel[] { overviewPanel };
754     }
755 
756     private static class MemoryOverviewPanel extends OverviewPanel {
MemoryOverviewPanel()757         MemoryOverviewPanel() {
758             super(Messages.HEAP_MEMORY_USAGE, usedKey, Messages.USED, Plotter.Unit.BYTES);
759         }
760 
updateMemoryInfo(long used, long committed, long max)761         private void updateMemoryInfo(long used, long committed, long max) {
762             getInfoLabel().setText(Resources.format(Messages.MEMORY_TAB_INFO_LABEL_FORMAT,
763                                                     formatBytes(used, true),
764                                                     formatBytes(committed, true),
765                                                     formatBytes(max, true)));
766         }
767     }
768 }
769