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 memoryPools = ManagementFactory.getMemoryPoolMXBeans();

    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());
    }
}

}