Java is not as simple as first impressions. With autoboxing and generics the language does not always operate as the user might expect. This together with the complex optimizations that the JVM makes, often just-in-time, for concurrent programs means poorly written code will not always work the same everywhere.
The java memory model is one of the things that can make concurrent java programming hard. If a Java program is not synchronized correctly then assignment statements can be run in a different order to the way the code is written.
- Java Language Specification: Threads and Locks
- Java Memory Model Page
- Doug Lea's Cookbook for Compiler Writers gives a machine level view of instruction reordering.
Generics can be complex, with type erasure and wildcards seemingly simple constructs won't compile. The Generics FAQ is over 500 pages.
- Josh Bloch has a presentation on some of the issues.
Wildcard capture is a common area where people occasionally get unstuck. The compiler complaining
capture of ?. This occurs when methods are unable to infer whether types specified by wildcards are identical or not. Type parameters can be used instead to resolve the issue.
Garbage Collection and memory management
Java memory allocation includes automatic management: memory which is dynamically allocated is freed automatically when it is no longer referenced. This removes a whole class of programming error that exists in other languages such as:
- Having to manage deallocation in the program.
- Risks of unfreed/unfreeable memory
- Dangling pointers: pointers which still point to freed memory.
- Elaborate designs to manage memory as a resource.
By managing memory and references to memory it is possible for the virtual machine to move memory about making better use of the heap. It can be easy to assume that garbage collection will under-perform compared with explicit allocate/free. Some older malloc/free implementations are slow, other newer allocators struggle with heap fragmentation and/or contention from multiple threads. Java garbage collectors are able to reorder the heap to defragment. Measures are also in place to cater for multi threading.
Explicit memory allocators and heap-fragmentation can also make it difficult to take advantage of the realities of machine architecture. Most systems have memory of differing speeds, and caches which fetch areas of memory. Caches fetch areas of memory, so well laid out memory can cause great improvements in applications by lowering the need for cache loads. In principle the VM with the garbage collector can reorder memory to improve application speed.
Garbage collection performance degrades considerably if the heap is almost full. Heaps should be sized for roughly 50% utilization. Too large a heap means that collection times could be needlessly longer.
Memory leaks can still occur in Java. For example long living collections holding onto object references can cause references to be forgotten about. Equally memory leaks can occur when objects refer to-one-another in unreachable ways.
To help with leaks Java provides special types of references:
- A soft reference contains a timestamp of when the reference was last accessed. When GC occurs the decision to clear a soft reference is based on: how old the timestamp is, and how much free space there is. Soft-references will be kept for at least one GC. The descision can be influenced by setting: -XX:SoftRefLRUPolicyMSPerMB=n
- is a reference to an object, that when it becomes the only reference left to the object the GC will aggressively clear.