Memory Management và Garbage Collection trong Java
1. Kiến Trúc Bộ Nhớ Java
1.1 JVM Memory Areas
- Heap Memory:
- Nơi lưu trữ objects
- Được chia thành Young và Old Generation
-
Được quản lý bởi Garbage Collector
-
Stack Memory:
- Lưu trữ method frames
- Local variables và references
-
Thread-specific
-
Metaspace (Java 8+):
- Thay thế PermGen
- Lưu trữ class metadata
- Không giới hạn cố định
public class MemoryAreas {
// Stack variables
public void stackExample() {
int x = 10; // Primitive trên stack
String s = "Hello"; // Reference trên stack
Object obj = new Object(); // Object trên heap
}
// Heap allocation
public void heapExample() {
// Arrays trên heap
int[] array = new int[1000];
// Objects trên heap
List<String> list = new ArrayList<>();
list.add("Item");
// String pool
String s1 = "Hello"; // String pool
String s2 = new String("Hello"); // Heap
}
}
1.2 Memory Leaks
- Nguyên nhân phổ biến:
- Static Collections
- Unclosed Resources
- Inner Class References
- ThreadLocal Variables
public class MemoryLeakExamples {
// 1. Static Collection Leak
private static final List<Object> staticList =
new ArrayList<>();
public void addToStaticList(Object obj) {
staticList.add(obj); // Objects never removed
}
// 2. Resource Leak
public void resourceLeak() {
try {
FileInputStream fis = new FileInputStream("file.txt");
// Missing close() call
} catch (IOException e) {
e.printStackTrace();
}
}
// 3. Inner Class Leak
public class InnerClassLeak {
private byte[] data = new byte[10000];
public Runnable getRunner() {
// Anonymous class holds reference to outer instance
return new Runnable() {
@Override
public void run() {
System.out.println(data.length);
}
};
}
}
// 4. ThreadLocal Leak
private static ThreadLocal<byte[]> threadLocal =
new ThreadLocal<>();
public void threadLocalLeak() {
threadLocal.set(new byte[10000]);
// Missing remove() call
}
}
2. Garbage Collection
2.1 GC Algorithms
- Serial GC:
- Single-threaded
- Stop-the-world
-
Phù hợp cho ứng dụng nhỏ
-
Parallel GC:
- Multi-threaded
- Stop-the-world
-
Tối ưu throughput
-
CMS GC:
- Concurrent Mark Sweep
- Giảm pause time
-
Phức tạp hơn
-
G1 GC:
- Garbage First
- Region-based
- Predictable pause times
public class GCConfigurations {
public static void main(String[] args) {
// Serial GC
// -XX:+UseSerialGC
// Parallel GC
// -XX:+UseParallelGC
// -XX:ParallelGCThreads=4
// CMS GC
// -XX:+UseConcMarkSweepGC
// -XX:CMSInitiatingOccupancyFraction=75
// G1 GC
// -XX:+UseG1GC
// -XX:MaxGCPauseMillis=200
}
}
2.2 GC Tuning
- Heap Size:
- Initial Heap Size (-Xms)
- Maximum Heap Size (-Xmx)
-
Young Generation Size (-Xmn)
-
GC Logging:
- Enable GC Logging
- Analyze GC Behavior
- Monitor Performance
public class GCTuning {
public static void main(String[] args) {
// Heap size configuration
// -Xms2g -Xmx4g -Xmn1g
// GC logging (Java 8)
// -XX:+PrintGCDetails
// -XX:+PrintGCDateStamps
// -Xloggc:gc.log
// GC logging (Java 9+)
// -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=10m
}
}
3. Memory Management Best Practices
3.1 Object Lifecycle Management
public class ObjectLifecycle {
// 1. Proper resource management
public void resourceManagement() {
// Try-with-resources
try (FileInputStream fis = new FileInputStream("file.txt");
BufferedReader reader = new BufferedReader(
new InputStreamReader(fis))) {
String line;
while ((line = reader.readLine()) != null) {
// Process line
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 2. Clear object references
public class Cache {
private Map<String, SoftReference<byte[]>> cache =
new HashMap<>();
public void clearEntry(String key) {
SoftReference<byte[]> ref = cache.remove(key);
if (ref != null) {
ref.clear(); // Clear reference
}
}
}
// 3. Avoid finalizers
public class Modern {
private FileInputStream fis;
// Use Cleaner instead of finalize()
private static final Cleaner cleaner = Cleaner.create();
public Modern(String path) throws IOException {
this.fis = new FileInputStream(path);
cleaner.register(this, () -> {
try {
fis.close();
} catch (IOException e) {
// Handle exception
}
});
}
}
}
3.2 Collection Usage
public class CollectionManagement {
// 1. Choose appropriate collection type
public void collectionChoice() {
// Fixed size - use Array
String[] array = new String[10];
// Dynamic size - use ArrayList
List<String> list = new ArrayList<>();
// Frequent insertions/deletions - use LinkedList
List<String> linkedList = new LinkedList<>();
// Unique elements - use HashSet
Set<String> set = new HashSet<>();
}
// 2. Initial capacity setting
public void capacityManagement() {
// Known size - set initial capacity
List<String> list = new ArrayList<>(1000);
Map<String, String> map = new HashMap<>(1000, 0.75f);
}
// 3. Clear collections properly
public void clearCollections() {
List<byte[]> list = new ArrayList<>();
// Fill list
// Clear and null references
list.clear();
list = null;
}
}
3.3 Memory-Sensitive Design
public class MemoryEfficientDesign {
// 1. Use primitive types when possible
public class User {
// BAD
private Integer age; // 16 bytes
private Boolean active; // 16 bytes
// GOOD
private int age; // 4 bytes
private boolean active; // 1 byte
}
// 2. String handling
public void stringHandling() {
// BAD - creates many temporary objects
String result = "";
for (int i = 0; i < 100; i++) {
result += "item" + i;
}
// GOOD - uses StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.append("item").append(i);
}
String result = sb.toString();
}
// 3. Lazy initialization
public class LazyInit {
private byte[] data;
public byte[] getData() {
if (data == null) {
data = loadData();
}
return data;
}
private byte[] loadData() {
// Load data
return new byte[1000];
}
}
}
4. Monitoring và Troubleshooting
4.1 Monitoring Tools
- JDK Tools:
- jstat: GC statistics
- jmap: Heap dumps
- jstack: Thread dumps
- jcmd: Diagnostic commands
public class MonitoringExample {
public static void main(String[] args) {
// Enable JMX monitoring
// -Dcom.sun.management.jmxremote
// -Dcom.sun.management.jmxremote.port=9010
// -Dcom.sun.management.jmxremote.authenticate=false
// -Dcom.sun.management.jmxremote.ssl=false
// Create heap dump
// jmap -dump:format=b,file=heap.bin <pid>
// Analyze GC
// jstat -gc <pid> 1000
}
}
4.2 Memory Analysis
public class MemoryAnalysis {
// 1. Heap dump analysis
public void createHeapDump() {
try {
MBeanServer server =
ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean mxBean =
ManagementFactory.newPlatformMXBeanProxy(
server,
"com.sun.management:type=HotSpotDiagnostic",
HotSpotDiagnosticMXBean.class);
mxBean.dumpHeap("heap.hprof", true);
} catch (Exception e) {
e.printStackTrace();
}
}
// 2. Memory leak detection
public class LeakDetector {
private Map<String, WeakReference<byte[]>> cache =
new WeakHashMap<>();
public void monitor() {
Runtime rt = Runtime.getRuntime();
long usedMB = (rt.totalMemory() - rt.freeMemory())
/ 1024 / 1024;
System.out.println("Used memory: " + usedMB + "MB");
}
}
}
4.3 Performance Tuning
```java
public class PerformanceTuning {
// 1. Memory pool monitoring
public void monitorMemoryPools() {
List
for (MemoryPoolMXBean pool : memoryPools) {
System.out.println("Pool: " + pool.getName());
System.out.println("Usage: " + pool.getUsage());
}
}
// 2. GC monitoring
public void monitorGC() {
List<GarbageCollectorMXBean> gcBeans =
ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("GC: " + gcBean.getName());
System.out.println("Count: " + gcBean.getCollectionCount());
System.out.println("Time: " + gcBean.getCollectionTime());
}
}
// 3. Thread monitoring
public void monitorThreads() {
ThreadMXBean threadBean =
ManagementFactory.getThreadMXBean();
long[] threadIds = threadBean.getAllThreadIds();
for (long threadId : threadIds) {
ThreadInfo info = threadBean.getThreadInfo(threadId);
System.out.println("Thread: " + info.getThreadName());
System.out.println("State: " + info.getThreadState());
}
}
}