-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add memory usage records to the JSON report (#1857)
- Loading branch information
1 parent
9e25044
commit fa91ddc
Showing
20 changed files
with
1,018 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
core/src/main/java/org/mobilitydata/gtfsvalidator/performance/MemoryMonitor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.mobilitydata.gtfsvalidator.performance; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* Annotation to monitor memory usage of a method. The annotated method should return a {@link | ||
* MemoryUsage} object. The key is used to group memory usage of different methods. | ||
*/ | ||
@Target(ElementType.METHOD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface MemoryMonitor { | ||
String key() default ""; | ||
} |
39 changes: 39 additions & 0 deletions
39
core/src/main/java/org/mobilitydata/gtfsvalidator/performance/MemoryMonitorAspect.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package org.mobilitydata.gtfsvalidator.performance; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
import org.aspectj.lang.ProceedingJoinPoint; | ||
import org.aspectj.lang.annotation.Around; | ||
import org.aspectj.lang.annotation.Aspect; | ||
import org.aspectj.lang.reflect.MethodSignature; | ||
|
||
/** Aspect to monitor memory usage of a method. */ | ||
@Aspect | ||
public class MemoryMonitorAspect { | ||
|
||
@Around("execution(@org.mobilitydata.gtfsvalidator.performance.MemoryMonitor * *(..))") | ||
public Object monitorMemoryUsage(ProceedingJoinPoint joinPoint) throws Throwable { | ||
String key = extractKey(joinPoint); | ||
MemoryUsage before = MemoryUsageRegister.getInstance().getMemoryUsageSnapshot(key, null); | ||
try { | ||
Object result = joinPoint.proceed(); | ||
return result; | ||
} finally { | ||
MemoryUsage after = MemoryUsageRegister.getInstance().getMemoryUsageSnapshot(key, before); | ||
MemoryUsageRegister.getInstance().registerMemoryUsage(after); | ||
} | ||
} | ||
|
||
/** | ||
* Extracts the key from the method signature or the annotation. | ||
* | ||
* @param joinPoint the join point | ||
* @return the key either from the annotation or the method signature. | ||
*/ | ||
private String extractKey(ProceedingJoinPoint joinPoint) { | ||
var method = ((MethodSignature) joinPoint.getSignature()).getMethod(); | ||
var memoryMonitor = method.getAnnotation(MemoryMonitor.class); | ||
return memoryMonitor != null && StringUtils.isNotBlank(memoryMonitor.key()) | ||
? memoryMonitor.key() | ||
: method.getDeclaringClass().getCanonicalName() + "." + method.getName(); | ||
} | ||
} |
176 changes: 176 additions & 0 deletions
176
core/src/main/java/org/mobilitydata/gtfsvalidator/performance/MemoryUsage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
package org.mobilitydata.gtfsvalidator.performance; | ||
|
||
import java.text.DecimalFormat; | ||
import org.apache.commons.lang3.StringUtils; | ||
|
||
/** Represents memory usage information. */ | ||
public class MemoryUsage { | ||
private static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00"); | ||
|
||
private String key; | ||
private long totalMemory; | ||
private long freeMemory; | ||
private long maxMemory; | ||
private Long diffMemory; | ||
|
||
public MemoryUsage() {} | ||
|
||
public MemoryUsage( | ||
String key, long totalMemory, long freeMemory, long maxMemory, Long diffMemory) { | ||
this.key = key; | ||
this.totalMemory = totalMemory; | ||
this.freeMemory = freeMemory; | ||
this.maxMemory = maxMemory; | ||
this.diffMemory = diffMemory; | ||
} | ||
|
||
/** | ||
* Converts bytes to human-readable memory. | ||
* | ||
* @param bytes | ||
* @return human-readable memory, e.g., "1.23 GiB" | ||
*/ | ||
public static String convertToHumanReadableMemory(Long bytes) { | ||
if (bytes == null) { | ||
return "N/A"; | ||
} | ||
long size = Math.abs(bytes); | ||
if (size < 1024) { | ||
return bytes + " bytes"; | ||
} | ||
if (size < 1048576) { | ||
return TWO_DECIMAL_FORMAT.format(Math.copySign(size / 1024.0, bytes)) + " KiB"; | ||
} | ||
if (size < 1073741824) { | ||
return TWO_DECIMAL_FORMAT.format(Math.copySign(size / 1048576.0, bytes)) + " MiB"; | ||
} | ||
if (size < 1099511627776L) { | ||
return TWO_DECIMAL_FORMAT.format(Math.copySign(size / 1073741824.0, bytes)) + " GiB"; | ||
} | ||
return TWO_DECIMAL_FORMAT.format(Math.copySign(size / 1099511627776L, bytes)) + " TiB"; | ||
} | ||
|
||
/** | ||
* The memory used is computed as the difference between the total memory and the free memory. | ||
* | ||
* @return the memory used. | ||
*/ | ||
public long usedMemory() { | ||
return totalMemory - freeMemory; | ||
} | ||
|
||
/** | ||
* Returns a human-readable string representation of the memory usage. | ||
* | ||
* @return a human-readable string representation of the memory usage. | ||
*/ | ||
public String humanReadablePrint() { | ||
StringBuffer result = new StringBuffer(); | ||
result.append("Memory usage registered"); | ||
if (StringUtils.isNotBlank(key)) { | ||
result.append(" for key: ").append(key); | ||
} else { | ||
result.append(":"); | ||
} | ||
result.append(" Max: ").append(convertToHumanReadableMemory(maxMemory)); | ||
result.append(" Total: ").append(convertToHumanReadableMemory(totalMemory)); | ||
result.append(" Free: ").append(convertToHumanReadableMemory(freeMemory)); | ||
result.append(" Used: ").append(convertToHumanReadableMemory(usedMemory())); | ||
result.append(" Diff: ").append(convertToHumanReadableMemory(diffMemory)); | ||
return result.toString(); | ||
} | ||
|
||
public String getKey() { | ||
return key; | ||
} | ||
|
||
public void setKey(String key) { | ||
this.key = key; | ||
} | ||
|
||
public long getTotalMemory() { | ||
return totalMemory; | ||
} | ||
|
||
public void setTotalMemory(long totalMemory) { | ||
this.totalMemory = totalMemory; | ||
} | ||
|
||
public long getFreeMemory() { | ||
return freeMemory; | ||
} | ||
|
||
public void setFreeMemory(long freeMemory) { | ||
this.freeMemory = freeMemory; | ||
} | ||
|
||
public long getMaxMemory() { | ||
return maxMemory; | ||
} | ||
|
||
public void setMaxMemory(long maxMemory) { | ||
this.maxMemory = maxMemory; | ||
} | ||
|
||
public Long getDiffMemory() { | ||
return diffMemory; | ||
} | ||
|
||
public void setDiffMemory(Long diffMemory) { | ||
this.diffMemory = diffMemory; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "MemoryUsage{" | ||
+ "key=" | ||
+ key | ||
+ ", " | ||
+ "totalMemory=" | ||
+ totalMemory | ||
+ ", " | ||
+ "freeMemory=" | ||
+ freeMemory | ||
+ ", " | ||
+ "maxMemory=" | ||
+ maxMemory | ||
+ ", " | ||
+ "diffMemory=" | ||
+ diffMemory | ||
+ "}"; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (o == this) { | ||
return true; | ||
} | ||
if (o instanceof MemoryUsage) { | ||
MemoryUsage that = (MemoryUsage) o; | ||
return this.key.equals(that.getKey()) | ||
&& this.totalMemory == that.getTotalMemory() | ||
&& this.freeMemory == that.getFreeMemory() | ||
&& this.maxMemory == that.getMaxMemory() | ||
&& (this.diffMemory == null | ||
? that.getDiffMemory() == null | ||
: this.getDiffMemory().equals(that.getDiffMemory())); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
int h$ = 1; | ||
h$ *= 1000003; | ||
h$ ^= key.hashCode(); | ||
h$ *= 1000003; | ||
h$ ^= (int) ((totalMemory >>> 32) ^ totalMemory); | ||
h$ *= 1000003; | ||
h$ ^= (int) ((freeMemory >>> 32) ^ freeMemory); | ||
h$ *= 1000003; | ||
h$ ^= (int) ((maxMemory >>> 32) ^ maxMemory); | ||
h$ *= 1000003; | ||
h$ ^= (diffMemory == null) ? 0 : diffMemory.hashCode(); | ||
return h$; | ||
} | ||
} |
Oops, something went wrong.