PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/tagtraum/perf/gcviewer/model/GCModel.java

https://gitlab.com/PawelCzarnota/GCViewer
Java | 963 lines | 612 code | 141 blank | 210 comment | 83 complexity | f4dae69cca18c9227a0860326047f0d1 MD5 | raw file
  1. package com.tagtraum.perf.gcviewer.model;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.Serializable;
  6. import java.net.HttpURLConnection;
  7. import java.net.URL;
  8. import java.net.URLConnection;
  9. import java.time.Duration;
  10. import java.time.Instant;
  11. import java.time.ZonedDateTime;
  12. import java.time.temporal.ChronoUnit;
  13. import java.util.ArrayList;
  14. import java.util.Iterator;
  15. import java.util.List;
  16. import java.util.Map;
  17. import java.util.TreeMap;
  18. import java.util.logging.Level;
  19. import java.util.logging.Logger;
  20. import com.tagtraum.perf.gcviewer.math.DoubleData;
  21. import com.tagtraum.perf.gcviewer.math.IntData;
  22. import com.tagtraum.perf.gcviewer.math.RegressionLine;
  23. import com.tagtraum.perf.gcviewer.model.AbstractGCEvent.CollectionType;
  24. import com.tagtraum.perf.gcviewer.model.AbstractGCEvent.Generation;
  25. /**
  26. * Collection of GCEvents.
  27. *
  28. * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
  29. */
  30. public class GCModel implements Serializable {
  31. private static final long serialVersionUID = -6479685723904770990L;
  32. /**
  33. * Contains information about a file.
  34. *
  35. * @author <a href="mailto:gcviewer@gmx.ch">Joerg Wuethrich</a>
  36. */
  37. private static class FileInformation implements Serializable {
  38. private static final long serialVersionUID = 1L;
  39. public long lastModified;
  40. public long length;
  41. public FileInformation() {
  42. this(-1, -1);
  43. }
  44. public FileInformation(long lastModified, long length) {
  45. super();
  46. this.lastModified = lastModified;
  47. this.length = length;
  48. }
  49. public void setFileInformation(FileInformation other) {
  50. this.lastModified = other.lastModified;
  51. this.length = other.length;
  52. }
  53. /**
  54. * @see java.lang.Object#equals(java.lang.Object)
  55. */
  56. public boolean equals(Object other) {
  57. if (this == other) {
  58. return true;
  59. }
  60. if (other == null) {
  61. return false;
  62. }
  63. if (!(other instanceof FileInformation)) {
  64. return false;
  65. }
  66. FileInformation fileInfo = (FileInformation)other;
  67. return fileInfo.lastModified == lastModified
  68. && fileInfo.length == length;
  69. }
  70. @Override
  71. public String toString() {
  72. return FileInformation.class.toString() + "; lastModified=" + lastModified + ", length=" + length;
  73. }
  74. }
  75. private static final Logger LOG = Logger.getLogger(GCModel.class.getName());
  76. private List<AbstractGCEvent<?>> allEvents;
  77. private List<AbstractGCEvent<?>> stopTheWorldEvents;
  78. private List<GCEvent> gcEvents;
  79. private List<AbstractGCEvent<?>> vmOperationEvents;
  80. private List<ConcurrentGCEvent> concurrentGCEvents;
  81. private List<GCEvent> currentNoFullGCEvents;
  82. private List<GCEvent> fullGCEvents;
  83. private FileInformation fileInformation = new FileInformation();
  84. private Map<String, DoubleData> fullGcEventPauses; // pause information about all full gc events for detailed output
  85. private Map<String, DoubleData> gcEventPauses; // pause information about all stw events for detailed output
  86. private Map<String, DoubleData> concurrentGcEventPauses; // pause information about all concurrent events
  87. private Map<String, DoubleData> vmOperationEventPauses; // pause information about vm operations ("application stopped")
  88. private IntData heapAllocatedSizes; // allocated heap size of every event
  89. private IntData tenuredAllocatedSizes; // allocated tenured size of every event that has this information
  90. private IntData youngAllocatedSizes; // allocated young size of every event that has this information
  91. private IntData permAllocatedSizes; // allocated perm size of every event that has this information
  92. private IntData heapUsedSizes; // used heap of every event
  93. private IntData tenuredUsedSizes; // used tenured size of every event that has this information
  94. private IntData youngUsedSizes; // used young size of every event that has this information
  95. private IntData permUsedSizes; // used perm size of every event that has this information
  96. private IntData postConcurrentCycleUsedTenuredSizes; // used tenured heap after concurrent collections
  97. private IntData postConcurrentCycleUsedHeapSizes; // used heap after concurrent collections
  98. private IntData promotion; // promotion from young to tenured generation during young collections
  99. private double firstPauseTimeStamp = Double.MAX_VALUE;
  100. private double lastPauseTimeStamp = 0;
  101. private DoubleData totalPause;
  102. private DoubleData fullGCPause;
  103. private double lastFullGcPauseTimeStamp = 0;
  104. private DoubleData fullGcPauseInterval; // interval between two stop the Full GC pauses
  105. private DoubleData gcPause; // not full gc but stop the world pause
  106. private DoubleData vmOperationPause; // "application stopped"
  107. private double lastGcPauseTimeStamp = 0;
  108. private DoubleData pauseInterval; // interval between two stop the world pauses
  109. private DoubleData initiatingOccupancyFraction; // all concurrent collectors; start of concurrent collection
  110. private long freedMemory;
  111. private Format format;
  112. private IntData postGCUsedMemory;
  113. private IntData postFullGCUsedHeap;
  114. private IntData freedMemoryByGC;
  115. private IntData freedMemoryByFullGC;
  116. private DoubleData postGCSlope;
  117. private RegressionLine currentPostGCSlope;
  118. private RegressionLine currentRelativePostGCIncrease;
  119. private DoubleData relativePostGCIncrease;
  120. private RegressionLine postFullGCSlope;
  121. private RegressionLine relativePostFullGCIncrease;
  122. private URL url;
  123. public GCModel() {
  124. this.allEvents = new ArrayList<AbstractGCEvent<?>>();
  125. this.stopTheWorldEvents = new ArrayList<AbstractGCEvent<?>>();
  126. this.gcEvents = new ArrayList<GCEvent>();
  127. this.vmOperationEvents = new ArrayList<AbstractGCEvent<?>>();
  128. this.concurrentGCEvents = new ArrayList<ConcurrentGCEvent>();
  129. this.fullGCEvents = new ArrayList<GCEvent>();
  130. this.currentNoFullGCEvents = new ArrayList<GCEvent>();
  131. this.currentPostGCSlope = new RegressionLine();
  132. this.postFullGCSlope = new RegressionLine();
  133. this.postGCSlope = new DoubleData();
  134. this.freedMemoryByGC = new IntData();
  135. this.freedMemoryByFullGC = new IntData();
  136. this.postFullGCUsedHeap = new IntData();
  137. this.postGCUsedMemory = new IntData();
  138. this.totalPause = new DoubleData();
  139. this.fullGCPause = new DoubleData();
  140. this.fullGcPauseInterval = new DoubleData();
  141. this.gcPause = new DoubleData();
  142. this.vmOperationPause = new DoubleData();
  143. this.pauseInterval = new DoubleData();
  144. this.initiatingOccupancyFraction = new DoubleData();
  145. this.currentRelativePostGCIncrease = new RegressionLine();
  146. this.relativePostGCIncrease = new DoubleData();
  147. this.relativePostFullGCIncrease = new RegressionLine();
  148. this.fullGcEventPauses = new TreeMap<String, DoubleData>();
  149. this.gcEventPauses = new TreeMap<String, DoubleData>();
  150. this.concurrentGcEventPauses = new TreeMap<String, DoubleData>();
  151. this.vmOperationEventPauses = new TreeMap<String, DoubleData>();
  152. this.heapAllocatedSizes = new IntData();
  153. this.permAllocatedSizes = new IntData();
  154. this.tenuredAllocatedSizes = new IntData();
  155. this.youngAllocatedSizes = new IntData();
  156. this.heapUsedSizes = new IntData();
  157. this.permUsedSizes = new IntData();
  158. this.tenuredUsedSizes = new IntData();
  159. this.youngUsedSizes = new IntData();
  160. this.postConcurrentCycleUsedTenuredSizes = new IntData();
  161. this.postConcurrentCycleUsedHeapSizes = new IntData();
  162. this.promotion = new IntData();
  163. }
  164. public long getLastModified() {
  165. return fileInformation.lastModified;
  166. }
  167. public URL getURL() {
  168. return url;
  169. }
  170. private void printPauseMap(Map<String, DoubleData> pauseMap) {
  171. for (Map.Entry<String, DoubleData> entry: pauseMap.entrySet()) {
  172. System.out.println(entry.getKey() + " [n, avg, sum, min, max]:\t" + entry.getValue().getN() + "\t" + entry.getValue().average() + "\t" + entry.getValue().getSum() + "\t" + entry.getValue().getMin() + "\t" + entry.getValue().getMax());
  173. }
  174. }
  175. private void printIntData(String name, IntData data) {
  176. try {
  177. System.out.println(name + " (n, avg, stddev, min, max):\t" + data.getN() + "\t" + data.average() + "\t" + data.standardDeviation() + "\t" + data.getMin() + "\t" + data.getMax());
  178. } catch (IllegalStateException e) {
  179. System.out.println(name + "\t" + e.toString());
  180. }
  181. }
  182. private void printDoubleData(String name, DoubleData data) {
  183. try {
  184. System.out.println(name + " (n, avg, stddev, min, max):\t" + data.getN() + "\t" + data.average() + "\t" + data.standardDeviation() + "\t" + data.getMin() + "\t" + data.getMax());
  185. } catch (IllegalStateException e) {
  186. System.out.println(name + "\t" + e.toString());
  187. }
  188. }
  189. public void printDetailedInformation() {
  190. // TODO delete
  191. printPauseMap(gcEventPauses);
  192. printPauseMap(fullGcEventPauses);
  193. printPauseMap(concurrentGcEventPauses);
  194. printPauseMap(vmOperationEventPauses);
  195. printDoubleData("initiatingOccupancyFraction", initiatingOccupancyFraction);
  196. printIntData("heap size used", heapUsedSizes);
  197. printIntData("perm size used", permUsedSizes);
  198. printIntData("tenured size used", tenuredUsedSizes);
  199. printIntData("young size used", youngUsedSizes);
  200. }
  201. private FileInformation readFileInformation(URL url) {
  202. FileInformation fileInformation = new FileInformation();
  203. URLConnection urlConnection = null;
  204. try {
  205. urlConnection = url.openConnection();
  206. if (url.getProtocol().startsWith("http")) {
  207. ((HttpURLConnection)urlConnection).setRequestMethod("HEAD");
  208. }
  209. try (InputStream inputStream = urlConnection.getInputStream()) {
  210. fileInformation.length = urlConnection.getContentLength();
  211. fileInformation.lastModified = urlConnection.getLastModified();
  212. }
  213. }
  214. catch (IOException e) {
  215. if (LOG.isLoggable(Level.WARNING)) LOG.log(Level.WARNING, "Failed to obtain age and length of URL " + url, e);
  216. }
  217. return fileInformation;
  218. }
  219. public void setURL(URL url) {
  220. this.url = url;
  221. this.fileInformation.setFileInformation(readFileInformation(url));
  222. }
  223. public boolean isDifferent(File otherFile) {
  224. // we just ignore the file name for now...
  225. FileInformation fileInformation = new FileInformation(otherFile.lastModified(), otherFile.length());
  226. return !this.fileInformation.equals(fileInformation);
  227. }
  228. public boolean isDifferent(URL otherURL) {
  229. FileInformation fileInfo = readFileInformation(otherURL);
  230. return !this.fileInformation.equals(fileInfo);
  231. }
  232. /**
  233. * Returns the event that was last added or <code>null</code> if there is none yet.
  234. *
  235. * @return last event or <code>null</code>
  236. */
  237. public AbstractGCEvent<?> getLastEventAdded() {
  238. if (allEvents.size() > 0) {
  239. return allEvents.get(allEvents.size()-1);
  240. }
  241. else {
  242. return null;
  243. }
  244. }
  245. /**
  246. * Returns an iterator to all stop the world events (everything that stops the vm to perfom
  247. * its action - includes vm operations of present).
  248. *
  249. * @return iterator to all stop the world events
  250. */
  251. public Iterator<AbstractGCEvent<?>> getStopTheWorldEvents() {
  252. return stopTheWorldEvents.iterator();
  253. }
  254. /**
  255. * Returns an iterator to all garbage collection events (without full gcs / vm operations).
  256. *
  257. * @return iterator to all gc events (without full gcs).
  258. */
  259. public Iterator<GCEvent> getGCEvents() {
  260. return gcEvents.iterator();
  261. }
  262. /**
  263. * Returns an iterator to all vm operation events.
  264. *
  265. * @return iterator to all vm operation events
  266. */
  267. public Iterator<AbstractGCEvent<?>> getVmOperationsEvents() {
  268. return vmOperationEvents.iterator();
  269. }
  270. /**
  271. * Returns an iterator to all concurrent gc events.
  272. *
  273. * @return iterator to all concurrent gc events.
  274. */
  275. public Iterator<ConcurrentGCEvent> getConcurrentGCEvents() {
  276. return concurrentGCEvents.iterator();
  277. }
  278. /**
  279. * Returns an iterator to all events in the order they were added to the model.
  280. *
  281. * @return iterator to all events
  282. */
  283. public Iterator<AbstractGCEvent<?>> getEvents() {
  284. return allEvents.iterator();
  285. }
  286. /**
  287. * Returns an iterator to all full gc events.
  288. *
  289. * @return iterator to all full gc events
  290. */
  291. public Iterator<GCEvent> getFullGCEvents() {
  292. return fullGCEvents.iterator();
  293. }
  294. private DoubleData getDoubleData(String key, Map<String, DoubleData> eventMap) {
  295. DoubleData data = eventMap.get(key);
  296. if (data == null) {
  297. data = new DoubleData();
  298. eventMap.put(key, data);
  299. }
  300. return data;
  301. }
  302. public void add(AbstractGCEvent<?> abstractEvent) {
  303. makeSureHasTimeStamp(abstractEvent);
  304. allEvents.add(abstractEvent);
  305. if (abstractEvent.isStopTheWorld()) {
  306. // totalPause must not be added here yet, because in case of vmOperationEvents, the
  307. // pause might be adjusted
  308. stopTheWorldEvents.add(abstractEvent);
  309. }
  310. if (abstractEvent instanceof ConcurrentGCEvent) {
  311. ConcurrentGCEvent concEvent = (ConcurrentGCEvent)abstractEvent;
  312. concurrentGCEvents.add(concEvent);
  313. DoubleData pauses = getDoubleData(concEvent.getExtendedType().getName(), concurrentGcEventPauses);
  314. pauses.add(concEvent.getPause());
  315. }
  316. else if (abstractEvent instanceof GCEvent) {
  317. // collect statistics about all stop the world events
  318. GCEvent event = (GCEvent) abstractEvent;
  319. updateHeapSizes(event);
  320. updateGcPauseInterval(event);
  321. updatePromotion(event);
  322. if (event.isInitialMark()) {
  323. updateInitiatingOccupancyFraction(event);
  324. }
  325. if (size() > 1 && allEvents.get(allEvents.size() - 2).isConcurrentCollectionEnd()) {
  326. updatePostConcurrentCycleUsedSizes(event);
  327. }
  328. freedMemory += event.getPreUsed() - event.getPostUsed();
  329. if (!event.isFull()) {
  330. // make a difference between stop the world events, which only collect from some generations...
  331. DoubleData pauses = getDoubleData(event.getTypeAsString(), gcEventPauses);
  332. pauses.add(event.getPause());
  333. gcEvents.add(event);
  334. postGCUsedMemory.add(event.getPostUsed());
  335. freedMemoryByGC.add(event.getPreUsed() - event.getPostUsed());
  336. currentNoFullGCEvents.add(event);
  337. currentPostGCSlope.addPoint(event.getTimestamp(), event.getPostUsed());
  338. currentRelativePostGCIncrease.addPoint(currentRelativePostGCIncrease.getPointCount(), event.getPostUsed());
  339. gcPause.add(event.getPause());
  340. }
  341. else {
  342. // ... as opposed to all generations
  343. DoubleData pauses = getDoubleData(event.getTypeAsString(), fullGcEventPauses);
  344. pauses.add(event.getPause());
  345. updateFullGcPauseInterval(event);
  346. fullGCEvents.add(event);
  347. postFullGCUsedHeap.add(event.getPostUsed());
  348. int freed = event.getPreUsed() - event.getPostUsed();
  349. freedMemoryByFullGC.add(freed);
  350. fullGCPause.add(event.getPause());
  351. postFullGCSlope.addPoint(event.getTimestamp(), event.getPostUsed());
  352. relativePostFullGCIncrease.addPoint(relativePostFullGCIncrease.getPointCount(), event.getPostUsed());
  353. // process no full-gc run data
  354. if (currentPostGCSlope.hasPoints()) {
  355. // make sure we have at least _two_ data points
  356. if (currentPostGCSlope.isLine()) {
  357. postGCSlope.add(currentPostGCSlope.slope(), currentPostGCSlope.getPointCount());
  358. relativePostGCIncrease.add(currentRelativePostGCIncrease.slope(), currentRelativePostGCIncrease.getPointCount());
  359. }
  360. currentPostGCSlope.reset();
  361. currentRelativePostGCIncrease.reset();
  362. }
  363. }
  364. }
  365. else if (abstractEvent instanceof VmOperationEvent) {
  366. adjustPause((VmOperationEvent) abstractEvent);
  367. if (abstractEvent.getTimestamp() < 0.000001) {
  368. setTimeStamp((VmOperationEvent) abstractEvent);
  369. }
  370. vmOperationPause.add(abstractEvent.getPause());
  371. vmOperationEvents.add(abstractEvent);
  372. DoubleData vmOpPauses = getDoubleData(abstractEvent.getTypeAsString(), vmOperationEventPauses);
  373. vmOpPauses.add(abstractEvent.getPause());
  374. }
  375. if (size() == 1 || (size() > 1 && abstractEvent.getTimestamp() > 0.0)) {
  376. // timestamp == 0 is only valid, if it is the first event.
  377. // sometimes, no timestamp is present, because the line is mixed -> don't count these here
  378. firstPauseTimeStamp = Math.min(firstPauseTimeStamp, abstractEvent.getTimestamp());
  379. }
  380. lastPauseTimeStamp = Math.max(lastPauseTimeStamp, abstractEvent.getTimestamp());
  381. if (abstractEvent.isStopTheWorld()) {
  382. // add to total pause here, because then adjusted VmOperationEvents are added correctly
  383. // as well
  384. totalPause.add(abstractEvent.getPause());
  385. }
  386. }
  387. private void makeSureHasTimeStamp(AbstractGCEvent<?> abstractEvent) {
  388. if (size() >= 1 && abstractEvent.getTimestamp() < 0.000001 && abstractEvent.getDatestamp() != null) {
  389. // looks like there is no timestamp set -> set one, because a lot depends on the timestamps
  390. abstractEvent.setTimestamp(ChronoUnit.MILLIS.between(getFirstDateStamp(), abstractEvent.getDatestamp()) / 1000.0);
  391. }
  392. }
  393. private void updatePostConcurrentCycleUsedSizes(GCEvent event) {
  394. // Most interesting is the size of the life objects immediately after a concurrent cycle.
  395. // Since the "concurrent-end" events don't have the heap size information, the next event
  396. // after is taken to get the information. Young generation, that has already filled up
  397. // again since the concurrent-end should not be counted, so take tenured size, if available.
  398. GCEvent afterConcurrentEvent = event;
  399. if (event.hasDetails()) {
  400. afterConcurrentEvent = event.getTenured();
  401. }
  402. postConcurrentCycleUsedTenuredSizes.add(afterConcurrentEvent.getPreUsed());
  403. postConcurrentCycleUsedHeapSizes.add(event.getPreUsed());
  404. }
  405. private void adjustPause(VmOperationEvent vmOpEvent) {
  406. if (stopTheWorldEvents.size() > 1) {
  407. AbstractGCEvent<?> previousEvent = stopTheWorldEvents.get(stopTheWorldEvents.size() - 2);
  408. // if the event directly before this event is also a VM_OPERATION event,
  409. // it was a VM_OPERATION without gc pause -> whole pause is "overhead"
  410. if (!previousEvent.getExtendedType().getCollectionType().equals(CollectionType.VM_OPERATION)) {
  411. // only count overhead of vmOpEvent, not whole pause,
  412. // because it includes the previous stop the world event
  413. double adjustedPause = vmOpEvent.getPause() - previousEvent.getPause();
  414. if (adjustedPause > 0) {
  415. vmOpEvent.setPause(adjustedPause);
  416. adjustTimeStamp(previousEvent, vmOpEvent);
  417. }
  418. else {
  419. // this happens if the first VM_OPERATION event after a GCEvent could not be read (mixed with concurrent event)
  420. // and the next is used to calculate the overhead
  421. LOG.fine("vmOpEvent at " + vmOpEvent.getTimestamp()
  422. + " should not have negative pause -> no adjustment made");
  423. }
  424. }
  425. }
  426. }
  427. /**
  428. * Make sure, time / datestamp of <code>vmOpEvent</code> is at least as long later as the
  429. * pause duration of <code>previousEvent</code>.
  430. *
  431. * @param previousEvent event just before <code>vmOpEvent</code>
  432. * @param vmOpEvent event to be adjusted
  433. */
  434. private void adjustTimeStamp(AbstractGCEvent<?> previousEvent, VmOperationEvent vmOpEvent) {
  435. if (previousEvent.getTimestamp() + previousEvent.getPause() > vmOpEvent.getTimestamp()) {
  436. vmOpEvent.setTimestamp(previousEvent.getTimestamp() + previousEvent.getPause());
  437. if (previousEvent.getDatestamp() != null) {
  438. Duration adjustment = Duration.ofMinutes((long) Math.rint(previousEvent.getPause() / 60))
  439. .plus((long) Math.rint(previousEvent.getPause()), ChronoUnit.SECONDS)
  440. .plus((long) Math.rint(previousEvent.getPause() * 1000), ChronoUnit.MILLIS);
  441. ZonedDateTime adjustedDatestamp = previousEvent.getDatestamp().plus(adjustment);
  442. vmOpEvent.setDateStamp(adjustedDatestamp);
  443. }
  444. }
  445. }
  446. private void setTimeStamp(VmOperationEvent vmOpEvent) {
  447. AbstractGCEvent<?> previousEvent = stopTheWorldEvents.size() > 1
  448. ? stopTheWorldEvents.get(stopTheWorldEvents.size() - 2)
  449. : null;
  450. if (previousEvent != null) {
  451. adjustTimeStamp(previousEvent, vmOpEvent);
  452. }
  453. }
  454. /**
  455. * Promotion is the amount of memory that is promoted from young to tenured space during
  456. * a collection of the young space.
  457. *
  458. * @param event
  459. */
  460. private void updatePromotion(GCEvent event) {
  461. if (event.getGeneration().equals(Generation.YOUNG) && event.hasDetails() && !event.isFull()) {
  462. GCEvent youngEvent = null;
  463. for (Iterator<GCEvent> i = event.details(); i.hasNext(); ) {
  464. GCEvent ev = i.next();
  465. if (ev.getGeneration().equals(Generation.YOUNG)) {
  466. youngEvent = ev;
  467. break;
  468. }
  469. }
  470. if (youngEvent != null) {
  471. promotion.add((youngEvent.getPreUsed() - youngEvent.getPostUsed())
  472. - (event.getPreUsed() - event.getPostUsed())
  473. );
  474. }
  475. }
  476. }
  477. private void updateGcPauseInterval(GCEvent event) {
  478. if (lastGcPauseTimeStamp > 0) {
  479. if (!event.isConcurrencyHelper()) {
  480. // JRockit sometimes has special timestamps that seem to go back in time,
  481. // omit them here
  482. if (event.getTimestamp() - lastGcPauseTimeStamp >= 0) {
  483. pauseInterval.add(event.getTimestamp() - lastGcPauseTimeStamp);
  484. }
  485. lastGcPauseTimeStamp = event.getTimestamp();
  486. }
  487. } else {
  488. // interval between startup of VM and first gc event should be omitted because
  489. // startup time of VM is included.
  490. lastGcPauseTimeStamp = event.getTimestamp();
  491. }
  492. }
  493. private void updateFullGcPauseInterval(GCEvent event) {
  494. if (lastFullGcPauseTimeStamp > 0) {
  495. if (!event.isConcurrencyHelper()) {
  496. // JRockit sometimes has special timestamps that seem to go back in time,
  497. // omit them here
  498. if (event.getTimestamp() - lastFullGcPauseTimeStamp >= 0) {
  499. fullGcPauseInterval.add(event.getTimestamp() - lastFullGcPauseTimeStamp);
  500. }
  501. lastFullGcPauseTimeStamp = event.getTimestamp();
  502. }
  503. } else {
  504. // interval between startup of VM and first gc event should be omitted because
  505. // startup time of VM is included.
  506. lastFullGcPauseTimeStamp = event.getTimestamp();
  507. }
  508. }
  509. private void updateInitiatingOccupancyFraction(GCEvent event) {
  510. GCEvent initialMarkEvent = event;
  511. if (event.hasDetails()) {
  512. Iterator<GCEvent> i = event.details();
  513. while (i.hasNext()) {
  514. GCEvent gcEvent = i.next();
  515. if (gcEvent.isInitialMark()) {
  516. initialMarkEvent = gcEvent;
  517. break;
  518. }
  519. }
  520. }
  521. // getTotal() returns 0 only if just the memory information could not be parsed
  522. // which can be the case with java 7 G1 algorithm (mixed with concurrent event)
  523. if (initialMarkEvent != null && initialMarkEvent.getTotal() > 0) {
  524. initiatingOccupancyFraction.add(initialMarkEvent.getPreUsed() / (double)initialMarkEvent.getTotal());
  525. }
  526. }
  527. private void updateHeapSizes(GCEvent event) {
  528. // event always contains heap size
  529. if (event.getTotal() > 0) {
  530. heapAllocatedSizes.add(event.getTotal());
  531. heapUsedSizes.add(event.getPreUsed());
  532. }
  533. if (event.hasDetails()) {
  534. // if details are present, young and tenured are always assumed to be present
  535. // because one can be derived from the other
  536. GCEvent young = event.getYoung();
  537. if (young != null) {
  538. youngAllocatedSizes.add(young.getTotal());
  539. youngUsedSizes.add(young.getPreUsed());
  540. }
  541. GCEvent tenured = event.getTenured();
  542. if (tenured != null) {
  543. tenuredAllocatedSizes.add(tenured.getTotal());
  544. tenuredUsedSizes.add(tenured.getPreUsed());
  545. }
  546. GCEvent perm = event.getPerm();
  547. if (perm != null) {
  548. permAllocatedSizes.add(perm.getTotal());
  549. permUsedSizes.add(perm.getPreUsed());
  550. }
  551. }
  552. }
  553. public int size() {
  554. return allEvents.size();
  555. }
  556. /**
  557. * Get all types of events in the order they were added to the model.
  558. *
  559. * @param index index of event
  560. * @return event at <code>index</code>
  561. * @throws IndexOutOfBoundsException if <code>index</code> is out of bounds
  562. */
  563. public AbstractGCEvent<?> get(int index) {
  564. return allEvents.get(index);
  565. }
  566. /**
  567. * @return Statistical data about pauses caused by full garbage collections.
  568. */
  569. public DoubleData getFullGCPause() {
  570. return fullGCPause;
  571. }
  572. /**
  573. * @return Statistical data about pauses interval between full garbage collections.
  574. */
  575. public DoubleData getFullGCPauseInterval() {
  576. return fullGcPauseInterval;
  577. }
  578. /**
  579. * @return Statistical data about pauses caused by garbage collections (full gcs excluded).
  580. */
  581. public DoubleData getGCPause() {
  582. return gcPause;
  583. }
  584. /**
  585. * @return Statistical data about pauses caused by vm operations other than gc pauses ("application stopped").
  586. */
  587. public DoubleData getVmOperationPause() {
  588. return vmOperationPause;
  589. }
  590. /**
  591. * @return Interval between gc pauses (full gcs excluded).
  592. */
  593. public DoubleData getPauseInterval() {
  594. return pauseInterval;
  595. }
  596. /**
  597. * Return statistical data about fraction of tenured heap when concurrent collection cycles
  598. * are started.
  599. *
  600. * @return statistical data about tenured heap occupation at start of concurrent collections
  601. */
  602. public DoubleData getCmsInitiatingOccupancyFraction() {
  603. return initiatingOccupancyFraction;
  604. }
  605. /**
  606. * @return The increase in memory consumption after a full collection in relation to the amount that was
  607. * used after the previous full collection.
  608. */
  609. public RegressionLine getRelativePostFullGCIncrease() {
  610. return relativePostFullGCIncrease;
  611. }
  612. /**
  613. * @return The increase in memory consumption after a collection in relation to the amount that was
  614. * used after the previous collection.
  615. */
  616. public DoubleData getRelativePostGCIncrease() {
  617. return relativePostGCIncrease;
  618. }
  619. /**
  620. * @return The average slope of the regression lines of the memory consumption after
  621. * a garbage collection in between <em>full</em> garbage collections.
  622. * <p>
  623. * The unit is kb/s.
  624. */
  625. public double getPostGCSlope() {
  626. return postGCSlope.average();
  627. }
  628. public RegressionLine getCurrentPostGCSlope() {
  629. return currentPostGCSlope;
  630. }
  631. public RegressionLine getPostFullGCSlope() {
  632. return postFullGCSlope;
  633. }
  634. /**
  635. * @return Heap memory freed by a (small) garbage collection.
  636. */
  637. public IntData getFreedMemoryByGC() {
  638. return freedMemoryByGC;
  639. }
  640. /**
  641. * @return Heap memory freed by a <em>full</em> garbage collection.
  642. */
  643. public IntData getFreedMemoryByFullGC() {
  644. return freedMemoryByFullGC;
  645. }
  646. /**
  647. * @return Heap memory consumption after a (small) garbage collection.
  648. */
  649. public IntData getFootprintAfterGC() {
  650. return postGCUsedMemory;
  651. }
  652. /**
  653. * @return Heap memory consumption after a <em>full</em> garbage collection.
  654. */
  655. public IntData getFootprintAfterFullGC() {
  656. return postFullGCUsedHeap;
  657. }
  658. /**
  659. * @return Pause in sec.
  660. */
  661. public DoubleData getPause() {
  662. return totalPause;
  663. }
  664. public Map<String, DoubleData> getGcEventPauses() {
  665. return gcEventPauses;
  666. }
  667. public Map<String, DoubleData> getFullGcEventPauses() {
  668. return fullGcEventPauses;
  669. }
  670. public Map<String, DoubleData> getVmOperationEventPauses() {
  671. return vmOperationEventPauses;
  672. }
  673. public Map<String, DoubleData> getConcurrentEventPauses() {
  674. return concurrentGcEventPauses;
  675. }
  676. /**
  677. * @return Throughput in percent.
  678. */
  679. public double getThroughput() {
  680. return 100
  681. * (getRunningTime() - totalPause.getSum())
  682. / getRunningTime();
  683. }
  684. /**
  685. * @return max heap allocated for every event
  686. */
  687. public IntData getHeapAllocatedSizes() {
  688. return heapAllocatedSizes;
  689. }
  690. /**
  691. * @return max heap used for every event
  692. */
  693. public IntData getHeapUsedSizes() {
  694. return heapUsedSizes;
  695. }
  696. /**
  697. * @return perm sizes allocated for every event that contained one (only if detailed logging is active and
  698. * and all spaces were collected)
  699. */
  700. public IntData getPermAllocatedSizes() {
  701. return permAllocatedSizes;
  702. }
  703. /**
  704. * @return perm sizes used for every event that has the information
  705. */
  706. public IntData getPermUsedSizes() {
  707. return permUsedSizes;
  708. }
  709. /**
  710. * @return tenured sizes allocated for every event that contained one (only if detailed logging is active)
  711. */
  712. public IntData getTenuredAllocatedSizes() {
  713. return tenuredAllocatedSizes;
  714. }
  715. /**
  716. * @return tenured sizes used for every event that contained one (only if detailed logging is active)
  717. */
  718. public IntData getTenuredUsedSizes() {
  719. return tenuredUsedSizes;
  720. }
  721. /**
  722. * @return young sizes allocated for every event that contained one (only if detailed logging is active)
  723. */
  724. public IntData getYoungAllocatedSizes() {
  725. return youngAllocatedSizes;
  726. }
  727. /**
  728. * @return young sizes used for every event that contained one (only if detailed logging is active)
  729. */
  730. public IntData getYoungUsedSizes() {
  731. return youngUsedSizes;
  732. }
  733. /**
  734. * @return Sizes of tenured heap (or if not available total heap) immediately after completion of
  735. * a concurrent cycle.
  736. */
  737. public IntData getPostConcurrentCycleTenuredUsedSizes() {
  738. return postConcurrentCycleUsedTenuredSizes;
  739. }
  740. /**
  741. * @return Sizes of heap immediately after completion of a concurrent cycle.
  742. */
  743. public IntData getPostConcurrentCycleHeapUsedSizes() {
  744. return postConcurrentCycleUsedHeapSizes;
  745. }
  746. /**
  747. * @return promotion information for all young collections (how much memory was promoted to
  748. * tenured space per young collection?)
  749. */
  750. public IntData getPromotion() {
  751. return promotion;
  752. }
  753. /**
  754. * @return Footprint in KB.
  755. */
  756. public long getFootprint() {
  757. return heapAllocatedSizes.getMax();
  758. }
  759. /**
  760. * @return Running time in sec.
  761. */
  762. public double getRunningTime() {
  763. return lastPauseTimeStamp - firstPauseTimeStamp
  764. + (stopTheWorldEvents.size() > 0
  765. ? stopTheWorldEvents.get(stopTheWorldEvents.size() - 1).getPause()
  766. : 0);
  767. }
  768. /**
  769. * @return The timestamp of the first event in the log (which usually is probably never exactly 0)
  770. */
  771. public double getFirstPauseTimeStamp() {
  772. return firstPauseTimeStamp;
  773. }
  774. /**
  775. * @return The timestamp of the last event in the log
  776. */
  777. public double getLastPauseTimeStamp() {
  778. return lastPauseTimeStamp;
  779. }
  780. /**
  781. * @return Freed memory in KB.
  782. */
  783. public long getFreedMemory() {
  784. return freedMemory;
  785. }
  786. public Format getFormat() {
  787. return format;
  788. }
  789. public void setFormat(Format format) {
  790. this.format = format;
  791. }
  792. public boolean hasCorrectTimestamp() {
  793. return format == Format.IBM_VERBOSE_GC || format == Format.SUN_X_LOG_GC || format == Format.SUN_1_2_2VERBOSE_GC;
  794. }
  795. public boolean hasDateStamp() {
  796. return allEvents.size() > 0
  797. ? get(0).getDatestamp() != null
  798. : false;
  799. }
  800. public ZonedDateTime getFirstDateStamp() {
  801. return allEvents.size() > 0
  802. ? get(0).getDatestamp()
  803. : null;
  804. }
  805. public String toString() {
  806. return "GCModel[size=" + size() + "]: " + allEvents.toString();
  807. }
  808. public static class Format implements Serializable {
  809. private static final long serialVersionUID = 483615745336894207L;
  810. private String format;
  811. private Format(String format) {
  812. this.format = format;
  813. }
  814. public String toString() {
  815. return format;
  816. }
  817. public static final Format SUN_VERBOSE_GC = new Format("Sun -verbose:gc");
  818. public static final Format SUN_X_LOG_GC = new Format("Sun -Xloggc:<file>");
  819. public static final Format IBM_VERBOSE_GC = new Format("IBM -verbose:gc");
  820. public static final Format SUN_1_2_2VERBOSE_GC = new Format("Sun 1.2.2 -verbose:gc");
  821. }
  822. }