That one boring Saturday I wanted to learn something more about agents and thought it would be a really cool idea to “unfinalize” java.lang.String.
I started working on the project, developed simple transformer:
if (name.equals("java/lang/String")) { ClassPool classPool = ClassPool.getDefault(); final CtClass ctClass; try { ctClass = classPool.get(name.replace("/", ".")); } catch (NotFoundException e) { throw new RuntimeException(e); } int modifiers = ctClass.getModifiers(); if (Modifier.isFinal(modifiers)) { System.out.println(name + " modifiers: " + modifiers); ctClass.
A Java project with published container image that contains intentionally leaky native code to observe symptoms of a memory leak in Java in podman/docker or Kubernetes.
Native code intentionally “leaks” provided number of megabytes in a loop. The project runs by default with -XX:NativeMemoryTracking=summary enabled.
I wanted to observe how JVM will report native memory, crash and what pod and JVM metrics will look like.
Java doesn’t allocate many objects, almost none.
There are a few reasons why OOM might happen in a JVM. For some of them a JVM will crash with an option to write heap dump to a file system. None of us wants to get OOM on prod, and have to reconfigure deployments and hope for the worst to happen again, this time with some fallback plan.
In a JVM this can be configured with:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/heapDumpDirectory' but the problem is that each heap dump will be by default saved to a file with the same or similar name: /heapDumpDirectory/<process_id>.
In this post I’ll describe things you want to consider to let JVM use own ergonomic configuration, without drastically overriding them, for which you need more advanced tuning and more metrics.
Pod sizing for GC The limit numbers of processors and memory impacts how JVM will tune its own performance characteristics.
Most importantly it impacts what GC will be used and how many threads it will start to clean up memory, which impacts how frequent and long GC pauses are.
A less known thing about deploying a JVM in a container is what garbage collector will be set, if you do not specify one. Let’s look at the cases of JVM running in a container and see what GC will be set by default as I experiment with different Java versions and memory limits.
Java 8 - OpenJDK8-alpine With memory limit 1791Mb podman run --memory=1791m -ti openjdk:8-alpine java -XX:+PrintFlagsFinal -XX:+UseContainerSupport | grep 'Use.
That one time I have to extract, transform and load a massive CSV file into a bunch of database entities and it was kinda slow…
The class had position based CSV bindings, loaded into beans and streamed from a pretty big CSV file (+10Gb):
public class CSVUserEntry { @CsvBindByPosition(position = 0) private String userId; @CsvBindByPosition(position = 1) private String username; @CsvBindByPosition(position = 2) private String deviceId; @CsvBindByPosition(position = 3) private String keyAlias; @CsvBindByPosition(position = 4) private String passcodeKeyAlias; @CsvBindByPosition(position = 5) private String confirmationId; } Then I opened the stream in generic way, with a simple and fluent interfaces of Java stream API:
This post doesn’t contain full context of the works performed, only benchmarking part
I had to test how number of iterations impacts login request time to KeyCloak and if or how we can improve it. After investigating a few other options I decided to check what’s the difference for password hashing times using default hashing mechanism in KeyCloak. I found and extracted parts of password hashing mechanism from KeyCloak to my repo, developed small parametrised JMH benchmark, comparing:
I have this setup in a single project which handles backend and frontend generation of server and client code. This requires to run openapi-generator twice, once for backend with spring generator and once for frontend with typescript-angular generator. I need backend code to be generated to build directory - so it is not committed to version control. TypeScript code needs to be reformatted and committed to git.
TypeScript code also requires additional mapping due to non-standard structure of my specification.
@Test public void abstractClassesAreAbstract() { final JavaClasses importedClasses = new ClassFileImporter() .importPackages("net.agilob.project"); LoggingRulesTest.ABSTRACT_CLASS_MUST_BE_ABSTRACT.check(importedClasses); } public static final ArchRule ABSTRACT_CLASS_MUST_BE_ABSTRACT = classes() .that() .haveSimpleNameContaining("Abstract").or().haveSimpleNameContaining("abstract") .should() .haveModifier(JavaModifier.ABSTRACT);