Garbage Collector no Java: Guia Completo com Cenário Prático

🎭 A História: O Mistério da Aplicação Lenta

Era uma segunda-feira típica na empresa TechSolutions quando Marina, desenvolvedora sênior Java, recebeu uma ligação urgente do suporte técnico:

“Marina, a aplicação de e-commerce está com problemas sérios! Os usuários estão reclamando que as páginas demoram mais de 10 segundos para carregar, e às vezes o sistema trava completamente!”

Carlos, o desenvolvedor júnior da equipe, estava nervoso: “Eu não entendo, Marina. O código está funcionando perfeitamente no meu ambiente local. Será que é problema do servidor?”

Marina, com sua experiência, já suspeitava: “Carlos, me conte como você está gerenciando os objetos na aplicação. Especificamente, como está lidando com as listas de produtos e o cache de usuários?”

Carlos mostrou o código:

public class ProductCache {
    private static List<Product> allProducts = new ArrayList<>();
    
    public void loadProducts() {
        // Carrega produtos do banco sem limpar a lista anterior
        List<Product> newProducts = databaseService.getAllProducts();
        allProducts.addAll(newProducts); // PROBLEMA: Acumula objetos!
    }
}

Marina sorriu: “Encontrei o problema! Carlos, você está criando um vazamento de memória. Vamos resolver isso e eu vou te ensinar como o Garbage Collector funciona.”


📚 Entendendo o Garbage Collector

🔍 O que é o Garbage Collector?

Garbage Collector (GC) é um mecanismo automático de gerenciamento de memória que:

  • Remove objetos não utilizados da heap automaticamente
  • Libera espaço para novos objetos
  • Previne vazamentos de memória (na maioria dos casos)
  • Otimiza o uso de memória da aplicação

🧠 Como Funciona o GC?

Marina explicou para Carlos:

“O GC funciona em três etapas principais:”

  1. 🔍 Marcação (Mark): Identifica quais objetos ainda são referenciados
  2. 🧹 Limpeza (Sweep): Remove objetos não referenciados
  3. 📦 Compactação (Compact): Reorganiza a memória para evitar fragmentação

🏗️ Estrutura da Memória Java

┌─────────────────────────────────────────────────┐
│                    HEAP                         │
├─────────────────────────────────────────────────┤
│  Young Generation                               │
│  ├─ Eden Space (objetos novos)                  │
│  ├─ Survivor Space S0                          │
│  └─ Survivor Space S1                          │
├─────────────────────────────────────────────────┤
│  Old Generation (objetos que sobreviveram)     │
├─────────────────────────────────────────────────┤
│  Metaspace (metadados das classes)             │
└─────────────────────────────────────────────────┘

🚀 Principais Algoritmos de GC

1. Serial GC

  • Uso: Aplicações pequenas, single-thread
  • Heap: < 100MB
  • Latência: Alta
  • Throughput: Baixo

2. Parallel GC

  • Uso: Aplicações server com foco em throughput
  • Heap: 100MB – 4GB
  • Padrão: Java 8
  • Threads: Múltiplas

3. G1 GC

  • Uso: Aplicações com heaps grandes
  • Heap: > 4GB
  • Padrão: Java 9+
  • Latência: Baixa e previsível

4. ZGC/Shenandoah

  • Uso: Aplicações críticas
  • Heap: > 32GB
  • Latência: Ultra-baixa (< 10ms)
  • Concurrent: Totalmente paralelo

🛠️ Criando o Projeto no IntelliJ IDEA

📋 Passo a Passo

Marina orientou Carlos:

  1. Abrir IntelliJ IDEA (versão 2024.3 ou superior)
  2. File → New → Project
  3. Selecionar “Maven” como build system
  4. Configurar:
    • Namegc-demo-project
    • LocationC:\projetos\gc-demo-project
    • Language: Java
    • Build system: Maven
    • JDK: 21 (LTS)
    • Add sample code: ✅

📄 Configuração do pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.techsolutions</groupId>
    <artifactId>gc-demo-project</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- JUnit 5 para testes -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.1</version>
            <scope>test</scope>
        </dependency>
        
        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.9</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    
                    <target>21</target>
                </configuration>
            </plugin>
            
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.2</version>
            </plugin>
        </plugins>
    </build>
</project>


💻 Código Completo da Demonstração

📁 Estrutura do Projeto

gc-demo-project/
├── src/
│   ├── main/
│   │   └── java/
│   │       └── com/techsolutions/
│   │           ├── GarbageCollectorDemo.java
│   │           ├── model/
│   │           │   ├── Product.java
│   │           │   └── User.java
│   │           ├── cache/
│   │           │   ├── ProductCache.java
│   │           │   └── ImprovedProductCache.java
│   │           └── util/
│   │               └── MemoryMonitor.java
│   └── test/
│       └── java/
│           └── com/techsolutions/
│               └── GarbageCollectorTest.java
└── pom.xml

🏭 Classe Principal

package com.techsolutions;

import com.techsolutions.cache.ProductCache;
import com.techsolutions.cache.ImprovedProductCache;
import com.techsolutions.model.Product;
import com.techsolutions.model.User;
import com.techsolutions.util.MemoryMonitor;

import java.lang.ref.WeakReference;
import java.lang.ref.SoftReference;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Demonstração completa do funcionamento do Garbage Collector
 * 
 * @author Marina Santos & Carlos Silva
 * @version 1.0
 */
public class GarbageCollectorDemo {
    
    private static final MemoryMonitor memoryMonitor = new MemoryMonitor();
    
    public static void main(String[] args) {
        System.out.println("🚀 === DEMO: GARBAGE COLLECTOR EM AÇÃO ===");
        
        // Demonstra o problema original
        demonstrarProblema();
        
        // Demonstra a solução
        demonstrarSolucao();
        
        // Demonstra tipos de referências
        demonstrarTiposReferencias();
        
        // Demonstra monitoramento avançado
        demonstrarMonitoramentoAvancado();
        
        // Demonstra otimizações
        demonstrarOtimizacoes();
    }
    
    /**
     * Demonstra o problema original encontrado por Marina
     */
    private static void demonstrarProblema() {
        System.out.println("\n🔴 === PROBLEMA: VAZAMENTO DE MEMÓRIA ===");
        
        memoryMonitor.printMemoryInfo("Antes do problema");
        
        ProductCache cacheProblematico = new ProductCache();
        
        // Simula o problema: acúmulo de objetos
        for (int i = 0; i < 5; i++) {
            cacheProblematico.loadProducts();
            memoryMonitor.printMemoryInfo("Iteração " + (i + 1));
        }
        
        System.out.println("❌ Problema identificado: Objetos acumulando na memória!");
    }
    
    /**
     * Demonstra a solução implementada
     */
    private static void demonstrarSolucao() {
        System.out.println("\n✅ === SOLUÇÃO: CACHE OTIMIZADO ===");
        
        // Força GC para limpar memória do exemplo anterior
        System.gc();
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        memoryMonitor.printMemoryInfo("Após limpeza");
        
        ImprovedProductCache cacheOtimizado = new ImprovedProductCache();
        
        // Demonstra o cache otimizado
        for (int i = 0; i < 5; i++) {
            cacheOtimizado.loadProducts();
            memoryMonitor.printMemoryInfo("Iteração otimizada " + (i + 1));
        }
        
        System.out.println("✅ Solução implementada: Memória controlada!");
    }
    
    /**
     * Demonstra diferentes tipos de referências
     */
    private static void demonstrarTiposReferencias() {
        System.out.println("\n🔗 === TIPOS DE REFERÊNCIAS ===");
        
        // 1. Referência Forte (Strong Reference)
        User usuarioForte = new User("Carlos", "carlos@email.com");
        System.out.println("Referência Forte criada: " + usuarioForte.getName());
        
        // 2. Referência Fraca (Weak Reference)
        User usuarioTemp = new User("Temporário", "temp@email.com");
        WeakReference<User> referenciaFraca = new WeakReference<>(usuarioTemp);
        usuarioTemp = null; // Remove referência forte
        
        // 3. Referência Suave (Soft Reference)
        User usuarioCache = new User("Cache", "cache@email.com");
        SoftReference<User> referenciaSuave = new SoftReference<>(usuarioCache);
        usuarioCache = null; // Remove referência forte
        
        // 4. Referência Fantasma (Phantom Reference)
        ReferenceQueue<User> filaReferencias = new ReferenceQueue<>();
        User usuarioFantasma = new User("Fantasma", "fantasma@email.com");
        PhantomReference<User> referenciaFantasma = new PhantomReference<>(usuarioFantasma, filaReferencias);
        usuarioFantasma = null; // Remove referência forte
        
        // Força GC
        System.gc();
        
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // Verifica estado após GC
        System.out.println("Após GC:");
        System.out.println("- Referência Forte: " + usuarioForte.getName());
        System.out.println("- Referência Fraca: " + (referenciaFraca.get() != null ? referenciaFraca.get().getName() : "null"));
        System.out.println("- Referência Suave: " + (referenciaSuave.get() != null ? referenciaSuave.get().getName() : "null"));
        System.out.println("- Referência Fantasma: " + (referenciaFantasma.get() != null ? "Presente" : "null"));
    }
    
    /**
     * Demonstra monitoramento avançado do GC
     */
    private static void demonstrarMonitoramentoAvancado() {
        System.out.println("\n📊 === MONITORAMENTO AVANÇADO ===");
        
        // Obtém informações dos coletores de lixo
        List<java.lang.management.GarbageCollectorMXBean> gcBeans = 
            java.lang.management.ManagementFactory.getGarbageCollectorMXBeans();
        
        System.out.println("Coletores de Lixo ativos:");
        for (java.lang.management.GarbageCollectorMXBean gcBean : gcBeans) {
            System.out.println("- " + gcBean.getName() + 
                             " (Coletas: " + gcBean.getCollectionCount() + 
                             ", Tempo: " + gcBean.getCollectionTime() + "ms)");
        }
        
        // Informações da heap
        java.lang.management.MemoryMXBean memoryBean = 
            java.lang.management.ManagementFactory.getMemoryMXBean();
        
        System.out.println("\nInformações da Heap:");
        System.out.println("- Heap usada: " + formatBytes(memoryBean.getHeapMemoryUsage().getUsed()));
        System.out.println("- Heap máxima: " + formatBytes(memoryBean.getHeapMemoryUsage().getMax()));
        System.out.println("- Non-Heap usada: " + formatBytes(memoryBean.getNonHeapMemoryUsage().getUsed()));
    }
    
    /**
     * Demonstra otimizações para reduzir pressão no GC
     */
    private static void demonstrarOtimizacoes() {
        System.out.println("\n⚡ === OTIMIZAÇÕES PARA GC ===");
        
        // 1. Reutilização de objetos
        demonstrarReutilizacaoObjetos();
        
        // 2. String interning
        demonstrarStringInterning();
        
        // 3. Pools de objetos
        demonstrarObjectPools();
        
        // 4. Lazy initialization
        demonstrarLazyInitialization();
    }
    
    private static void demonstrarReutilizacaoObjetos() {
        System.out.println("\n1. Reutilização de StringBuilder:");
        
        // ❌ Forma ineficiente
        long startTime = System.nanoTime();
        String resultado = "";
        for (int i = 0; i < 1000; i++) {
            resultado += "Item " + i + " ";
        }
        long tempoIneficiente = System.nanoTime() - startTime;
        
        // ✅ Forma eficiente
        startTime = System.nanoTime();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 1000; i++) {
            sb.append("Item ").append(i).append(" ");
        }
        String resultadoEficiente = sb.toString();
        long tempoEficiente = System.nanoTime() - startTime;
        
        System.out.println("- Concatenação String: " + tempoIneficiente / 1_000_000 + "ms");
        System.out.println("- StringBuilder: " + tempoEficiente / 1_000_000 + "ms");
        System.out.println("- Melhoria: " + (tempoIneficiente / tempoEficiente) + "x mais rápido");
    }
    
    private static void demonstrarStringInterning() {
        System.out.println("\n2. String Interning:");
        
        String str1 = "Hello World";
        String str2 = "Hello World";
        String str3 = new String("Hello World");
        String str4 = str3.intern();
        
        System.out.println("- str1 == str2: " + (str1 == str2) + " (string literals)");
        System.out.println("- str1 == str3: " + (str1 == str3) + " (new String)");
        System.out.println("- str1 == str4: " + (str1 == str4) + " (após intern())");
    }
    
    private static void demonstrarObjectPools() {
        System.out.println("\n3. Object Pools:");
        
        // Exemplo simples de pool
        Queue<StringBuilder> pool = new ArrayDeque<>();
        
        // Inicializa pool
        for (int i = 0; i < 10; i++) {
            pool.offer(new StringBuilder());
        }
        
        // Usa objeto do pool
        StringBuilder sb = pool.poll();
        if (sb != null) {
            sb.append("Usando objeto do pool");
            System.out.println("- " + sb.toString());
            
            // Retorna ao pool após uso
            sb.setLength(0); // Limpa
            pool.offer(sb);
        }
        
        System.out.println("- Pool size: " + pool.size());
    }
    
    private static void demonstrarLazyInitialization() {
        System.out.println("\n4. Lazy Initialization:");
        
        class LazyResource {
            private volatile Map<String, String> cache;
            
            public Map<String, String> getCache() {
                if (cache == null) {
                    synchronized (this) {
                        if (cache == null) {
                            cache = new ConcurrentHashMap<>();
                            System.out.println("- Cache inicializado sob demanda");
                        }
                    }
                }
                return cache;
            }
        }
        
        LazyResource resource = new LazyResource();
        System.out.println("- Recurso criado (cache ainda não inicializado)");
        
        resource.getCache().put("key", "value");
        System.out.println("- Cache usado pela primeira vez");
    }
    
    private static String formatBytes(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
        return String.format("%.1f GB", bytes / (1024.0 * 1024.0 * 1024.0));
    }
}

🏷️ Classe Model – Product

package com.techsolutions.model;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * Classe que representa um produto no sistema
 */
public class Product {
    private Long id;
    private String name;
    private String description;
    private BigDecimal price;
    private String category;
    private LocalDateTime createdAt;
    
    public Product(Long id, String name, String description, BigDecimal price, String category) {
        this.id = id;
        this.name = name;
        this.description = description;
        this.price = price;
        this.category = category;
        this.createdAt = LocalDateTime.now();
    }
    
    // Getters e Setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
    
    public BigDecimal getPrice() { return price; }
    public void setPrice(BigDecimal price) { this.price = price; }
    
    public String getCategory() { return category; }
    public void setCategory(String category) { this.category = category; }
    
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Product product = (Product) o;
        return Objects.equals(id, product.id);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
    
    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", category='" + category + '\'' +
                '}';
    }
}

👤 Classe Model – User

package com.techsolutions.model;

import java.time.LocalDateTime;
import java.util.Objects;

/**
 * Classe que representa um usuário no sistema
 */
public class User {
    private String name;
    private String email;
    private LocalDateTime createdAt;
    
    public User(String name, String email) {
        this.name = name;
        this.email = email;
        this.createdAt = LocalDateTime.now();
    }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
    
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(email, user.email);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(email);
    }
    
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

🗂️ Cache Problemático

package com.techsolutions.cache;

import com.techsolutions.model.Product;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Cache problemático que causa vazamento de memória
 * Este é o código original que Carlos escreveu
 */
public class ProductCache {
    private static List&lt;Product> allProducts = new ArrayList&lt;>();
    private static final Random random = new Random();
    
    /**
     * ❌ PROBLEMA: Acumula objetos sem limpar
     */
    public void loadProducts() {
        System.out.println("🔄 Carregando produtos (método problemático)...");
        
        // Simula carregamento do banco de dados
        List&lt;Product> newProducts = simulateProductsFromDatabase();
        
        // PROBLEMA: Adiciona sem limpar a lista anterior
        allProducts.addAll(newProducts);
        
        System.out.println("📦 Total de produtos no cache: " + allProducts.size());
    }
    
    public List&lt;Product> getAllProducts() {
        return new ArrayList&lt;>(allProducts);
    }
    
    public int getCacheSize() {
        return allProducts.size();
    }
    
    /**
     * Simula busca no banco de dados
     */
    private List&lt;Product> simulateProductsFromDatabase() {
        List&lt;Product> products = new ArrayList&lt;>();
        
        for (int i = 0; i &lt; 1000; i++) {
            Product product = new Product(
                (long) i,
                "Produto " + i,
                "Descrição do produto " + i + " com detalhes extensos para ocupar mais memória",
                new BigDecimal(random.nextInt(1000) + 1),
                "Categoria " + (i % 10)
            );
            products.add(product);
        }
        
        return products;
    }
}

✅ Cache Otimizado

package com.techsolutions.cache;

import com.techsolutions.model.Product;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.lang.ref.SoftReference;

/**
 * Cache otimizado que resolve o problema de vazamento de memória
 * Esta é a solução que Marina propôs
 */
public class ImprovedProductCache {
    private final Map<String, SoftReference<List<Product>>> categoryCache = new ConcurrentHashMap<>();
    private final Map<Long, SoftReference<Product>> productCache = new ConcurrentHashMap<>();
    private static final Random random = new Random();
    private static final int MAX_CACHE_SIZE = 10000;
    
    /**
     * ✅ SOLUÇÃO: Gerencia memória adequadamente
     */
    public void loadProducts() {
        System.out.println("🔄 Carregando produtos (método otimizado)...");
        
        // Limpa cache antigo se necessário
        cleanupCache();
        
        // Simula carregamento do banco de dados
        List<Product> newProducts = simulateProductsFromDatabase();
        
        // Organiza por categoria usando SoftReference
        Map<String, List<Product>> productsByCategory = new HashMap<>();
        for (Product product : newProducts) {
            productsByCategory.computeIfAbsent(product.getCategory(), k -> new ArrayList<>()).add(product);
            
            // Cache individual de produtos
            productCache.put(product.getId(), new SoftReference<>(product));
        }
        
        // Atualiza cache por categoria
        for (Map.Entry<String, List<Product>> entry : productsByCategory.entrySet()) {
            categoryCache.put(entry.getKey(), new SoftReference<>(entry.getValue()));
        }
        
        System.out.println("📦 Cache atualizado - Categorias: " + categoryCache.size() + 
                          ", Produtos: " + productCache.size());
    }
    
    /**
     * Busca produtos por categoria
     */
    public List<Product> getProductsByCategory(String category) {
        SoftReference<List<Product>> ref = categoryCache.get(category);
        if (ref != null) {
            List<Product> products = ref.get();
            if (products != null) {
                return new ArrayList<>(products);
            } else {
                // Referência foi coletada pelo GC, remove do cache
                categoryCache.remove(category);
            }
        }
        return new ArrayList<>();
    }
    
    /**
     * Busca produto por ID
     */
    public Product getProductById(Long id) {
        SoftReference<Product> ref = productCache.get(id);
        if (ref != null) {
            Product product = ref.get();
            if (product != null) {
                return product;
            } else {
                // Referência foi coletada pelo GC, remove do cache
                productCache.remove(id);
            }
        }
        return null;
    }
    
    /**
     * Limpeza proativa do cache
     */
    private void cleanupCache() {
        // Remove referências vazias
        categoryCache.entrySet().removeIf(entry -> entry.getValue().get() == null);
        productCache.entrySet().removeIf(entry -> entry.getValue().get() == null);
        
        // Se ainda está muito grande, força limpeza
        if (productCache.size() > MAX_CACHE_SIZE) {
            System.out.println("🧹 Cache muito grande, forçando limpeza...");
            
            // Remove metade dos itens mais antigos (simulação)
            Iterator<Map.Entry<Long, SoftReference<Product>>> iterator = productCache.entrySet().iterator();
            int toRemove = productCache.size() / 2;
            int removed = 0;
            
            while (iterator.hasNext() && removed < toRemove) {
                iterator.next();
                iterator.remove();
                removed++;
            }
        }
    }
    
    /**
     * Informações do cache
     */
    public String getCacheInfo() {
        int activeCategoryCache = 0;
        int activeProductCache = 0;
        
        for (SoftReference<List<Product>> ref : categoryCache.values()) {
            if (ref.get() != null) activeCategoryCache++;
        }
        
        for (SoftReference<Product> ref : productCache.values()) {
            if (ref.get() != null) activeProductCache++;
        }
        
        return String.format("Cache Info - Categorias: %d/%d, Produtos: %d/%d",
                activeCategoryCache, categoryCache.size(),
                activeProductCache, productCache.size());
    }
    
    /**
     * Limpa todo o cache
     */
    public void clearCache() {
        categoryCache.clear();
        productCache.clear();
        System.out.println("🧹 Cache limpo completamente");
    }
    
    /**
     * Simula busca no banco de dados
     */
    private List<Product> simulateProductsFromDatabase() {
        List<Product> products = new ArrayList<>();
        
        for (int i = 0; i < 1000; i++) {
            Product product = new Product(
                (long) i,
                "Produto " + i,
                "Descrição do produto " + i,
                new BigDecimal(random.nextInt(1000) + 1),
                "Categoria " + (i % 10)
            );
            products.add(product);
        }
        
        return products;
    }
}

📊 Utilitário de Monitoramento

package com.techsolutions.util;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.text.DecimalFormat;

/**
 * Utilitário para monitoramento de memória e GC
 */
public class MemoryMonitor {
    private final MemoryMXBean memoryBean;
    private final DecimalFormat df = new DecimalFormat("#,##0.00");
    
    public MemoryMonitor() {
        this.memoryBean = ManagementFactory.getMemoryMXBean();
    }
    
    /**
     * Imprime informações detalhadas sobre o uso de memória
     */
    public void printMemoryInfo(String context) {
        System.out.println("\n📊 === MEMÓRIA: " + context + " ===");
        
        // Informações da Heap
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        System.out.println("🏠 Heap Memory:");
        System.out.println("  ├─ Usada: " + formatBytes(heapUsage.getUsed()));
        System.out.println("  ├─ Committed: " + formatBytes(heapUsage.getCommitted()));
        System.out.println("  ├─ Máxima: " + formatBytes(heapUsage.getMax()));
        System.out.println("  └─ Uso: " + df.format((double)heapUsage.getUsed() / heapUsage.getMax() * 100) + "%");
        
        // Informações Non-Heap
        MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
        System.out.println("🏢 Non-Heap Memory:");
        System.out.println("  ├─ Usada: " + formatBytes(nonHeapUsage.getUsed()));
        System.out.println("  ├─ Committed: " + formatBytes(nonHeapUsage.getCommitted()));
        System.out.println("  └─ Máxima: " + formatBytes(nonHeapUsage.getMax()));
        
        // Informações do Sistema
        Runtime runtime = Runtime.getRuntime();
        System.out.println("💻 Sistema:");
        System.out.println("  ├─ Processadores: " + runtime.availableProcessors());
        System.out.println("  ├─ Total JVM: " + formatBytes(runtime.totalMemory()));
        System.out.println("  ├─ Livre JVM: " + formatBytes(runtime.freeMemory()));
        System.out.println("  └─ Máxima JVM: " + formatBytes(runtime.maxMemory()));
    }
    
    /**
     * Obtém uso atual da heap em percentual
     */
    public double getHeapUsagePercent() {
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        return (double) heapUsage.getUsed() / heapUsage.getMax() * 100;
    }
    
    /**
     * Verifica se a memória está sob pressão
     */
    public boolean isMemoryUnderPressure() {
        return getHeapUsagePercent() > 80.0;
    }
    
    /**
     * Formata bytes para formato legível
     */
    private String formatBytes(long bytes) {
        if (bytes < 0) return "N/A";
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return df.format(bytes / 1024.0) + " KB";
        if (bytes < 1024 * 1024 * 1024) return df.format(bytes / (1024.0 * 1024.0)) + " MB";
        return df.format(bytes / (1024.0 * 1024.0 * 1024.0)) + " GB";
    }
}


🧪 Executando o Projeto

⚙️ Configurações de JVM para Teste

Marina ensinou Carlos a configurar diferentes GCs:

# 1. G1 GC (Recomendado para aplicações modernas)
java -XX:+UseG1GC -Xmx2g -Xms1g -XX:+PrintGC -XX:+PrintGCDetails com.techsolutions.GarbageCollectorDemo

# 2. Parallel GC
java -XX:+UseParallelGC -Xmx2g -Xms1g -XX:+PrintGC com.techsolutions.GarbageCollectorDemo

# 3. ZGC (Para teste com heap grande)
java -XX:+UseZGC -Xmx4g -Xms2g -XX:+PrintGC com.techsolutions.GarbageCollectorDemo

# 4. Monitoramento detalhado
java -XX:+UseG1GC -Xmx2g -Xms1g -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime com.techsolutions.GarbageCollectorDemo

🎯 Configuração no IntelliJ

  1. Run → Edit Configurations
  2. VM Options:-XX:+UseG1GC -Xmx2g -Xms1g -XX:+PrintGC -XX:+PrintGCDetails

🎉 Conclusão da História

Carlos, animado com os resultados: “Marina, incrível! A aplicação agora está muito mais rápida e estável. Aprendi muito sobre gerenciamento de memória!”

Marina sorriu: “Excelente, Carlos! Lembre-se sempre:”

🎯 Principais Lições

  1. 🔍 Monitore a Memória: Use ferramentas como VisualVM, JProfiler ou comando jstat
  2. 🧹 Limpe Referências: Sempre remova referências desnecessárias
  3. 💾 Use Caches Inteligentes: Implemente SoftReference ou WeakReference
  4. ⚡ Otimize Criação de Objetos: Reutilize objetos quando possível
  5. 📊 Teste Diferentes GCs: Cada aplicação tem necessidades específicas

🚀 Próximos Passos

  • Profile a aplicação regularmente
  • Monitore métricas de GC em produção
  • Teste diferentes configurações de heap
  • Implemente logs de GC para análise
  • Considere ferramentas como Micrometer para métricas avançadas

Carlos“Agora vou aplicar essas técnicas em todos os nossos projetos!”

Marina“Perfeito! E lembre-se: um bom desenvolvedor Java sempre conhece seu Garbage Collector!”


📚 Recursos Adicionais

🔧 Ferramentas Recomendadas

  • VisualVM: Profiling gratuito
  • JProfiler: Profiling profissional
  • GCEasy: Análise de logs GC online
  • Micrometer: Métricas de aplicação

📖 Leitura Complementar

  • Oracle Java GC Documentation
  • “Java Performance: The Definitive Guide” – Scott Oaks
  • “Optimizing Java” – Benjamin J. Evans

🎓 Certificações

  • Oracle Certified Professional Java SE
  • Oracle Certified Master Java SE

🧪 Testes Unitários

📄 Classe de Teste Principal

package com.techsolutions;

import com.techsolutions.cache.ImprovedProductCache;
import com.techsolutions.cache.ProductCache;
import com.techsolutions.model.Product;
import com.techsolutions.util.MemoryMonitor;
import org.junit.jupiter.api.*;
import java.lang.ref.WeakReference;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Testes para validar o comportamento do Garbage Collector
 */
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class GarbageCollectorTest {
    
    private MemoryMonitor memoryMonitor;
    private static final int LARGE_OBJECT_COUNT = 100000;
    
    @BeforeEach
    void setUp() {
        memoryMonitor = new MemoryMonitor();
        // Força GC antes de cada teste
        System.gc();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    
    @Test
    @Order(1)
    @DisplayName("Teste de Vazamento de Memória - Cache Problemático")
    void testMemoryLeakWithProblematicCache() {
        System.out.println("🔴 Testando cache problemático...");
        
        ProductCache problematicCache = new ProductCache();
        double initialMemory = memoryMonitor.getHeapUsagePercent();
        
        // Executa múltiplas vezes para acumular objetos
        for (int i = 0; i < 5; i++) {
            problematicCache.loadProducts();
        }
        
        double finalMemory = memoryMonitor.getHeapUsagePercent();
        int cacheSize = problematicCache.getCacheSize();
        
        System.out.println("Cache size: " + cacheSize);
        System.out.println("Memory increase: " + (finalMemory - initialMemory) + "%");
        
        // Verifica se houve acúmulo de objetos
        assertTrue(cacheSize > 1000, "Cache deve ter acumulado objetos");
        assertTrue(finalMemory > initialMemory, "Uso de memória deve ter aumentado");
    }
    
    @Test
    @Order(2)
    @DisplayName("Teste de Eficiência - Cache Otimizado")
    void testOptimizedCache() {
        System.out.println("✅ Testando cache otimizado...");
        
        ImprovedProductCache optimizedCache = new ImprovedProductCache();
        double initialMemory = memoryMonitor.getHeapUsagePercent();
        
        // Executa múltiplas vezes
        for (int i = 0; i < 5; i++) {
            optimizedCache.loadProducts();
        }
        
        double finalMemory = memoryMonitor.getHeapUsagePercent();
        String cacheInfo = optimizedCache.getCacheInfo();
        
        System.out.println("Cache info: " + cacheInfo);
        System.out.println("Memory increase: " + (finalMemory - initialMemory) + "%");
        
        // Verifica se o cache está controlado
        assertNotNull(cacheInfo);
        // Memória não deve crescer descontroladamente
        assertTrue(finalMemory - initialMemory < 50, "Aumento de memória deve ser controlado");
    }
    
    @Test
    @Order(3)
    @DisplayName("Teste de Weak References")
    void testWeakReferences() {
        System.out.println("🔗 Testando weak references...");
        
        Product product = new Product(1L, "Test Product", "Description", 
                                    new BigDecimal("99.99"), "Test Category");
        
        WeakReference<Product> weakRef = new WeakReference<>(product);
        
        // Verifica se referência está presente
        assertNotNull(weakRef.get(), "Weak reference deve estar presente inicialmente");
        
        // Remove referência forte
        product = null;
        
        // Força GC múltiplas vezes
        for (int i = 0; i < 10; i++) {
            System.gc();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            
            if (weakRef.get() == null) {
                System.out.println("✅ Weak reference coletada após " + (i + 1) + " tentativas");
                break;
            }
        }
        
        // Weak reference pode ou não ter sido coletada (depende do GC)
        System.out.println("Weak reference final state: " + 
                          (weakRef.get() != null ? "Ainda presente" : "Coletada"));
    }
    
    @Test
    @Order(4)
    @DisplayName("Teste de Stress de Memória")
    void testMemoryStress() {
        System.out.println("💪 Testando stress de memória...");
        
        double initialMemory = memoryMonitor.getHeapUsagePercent();
        List<Product> products = new ArrayList<>();
        
        try {
            // Cria muitos objetos
            for (int i = 0; i < LARGE_OBJECT_COUNT; i++) {
                Product product = new Product(
                    (long) i,
                    "Product " + i,
                    "Long description for product " + i + " with lots of text to consume memory",
                    new BigDecimal(i),
                    "Category " + (i % 100)
                );
                products.add(product);
                
                // Monitora pressão de memória
                if (i % 10000 == 0) {
                    double currentMemory = memoryMonitor.getHeapUsagePercent();
                    System.out.println("Created " + i + " objects, memory: " + 
                                     String.format("%.1f%%", currentMemory));
                    
                    if (memoryMonitor.isMemoryUnderPressure()) {
                        System.out.println("⚠️  Memory under pressure, stopping creation");
                        break;
                    }
                }
            }
            
            double peakMemory = memoryMonitor.getHeapUsagePercent();
            System.out.println("Peak memory usage: " + String.format("%.1f%%", peakMemory));
            
            // Limpa referências
            products.clear();
            products = null;
            
            // Força GC
            System.gc();
            Thread.sleep(1000);
            
            double finalMemory = memoryMonitor.getHeapUsagePercent();
            System.out.println("Final memory usage: " + String.format("%.1f%%", finalMemory));
            
            // Verifica se memória foi liberada
            assertTrue(finalMemory < peakMemory, "Memória deve ter sido liberada após GC");
            
        } catch (OutOfMemoryError e) {
            System.out.println("❌ OutOfMemoryError occurred - this is expected in stress test");
            fail("Memory stress test failed with OutOfMemoryError");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            fail("Test interrupted");
        }
    }
    
    @Test
    @Order(5)
    @DisplayName("Teste de Performance StringBuilder vs String")
    void testStringBuilderPerformance() {
        System.out.println("⚡ Testando performance String vs StringBuilder...");
        
        int iterations = 10000;
        
        // Teste com concatenação String
        long startTime = System.nanoTime();
        String result = "";
        for (int i = 0; i < iterations; i++) {
            result += "Item " + i + " ";
        }
        long stringTime = System.nanoTime() - startTime;
        
        // Força GC para limpar objetos String temporários
        System.gc();
        
        // Teste com StringBuilder
        startTime = System.nanoTime();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < iterations; i++) {
            sb.append("Item ").append(i).append(" ");
        }
        String sbResult = sb.toString();
        long sbTime = System.nanoTime() - startTime;
        
        System.out.println("String concatenation: " + (stringTime / 1_000_000) + "ms");
        System.out.println("StringBuilder: " + (sbTime / 1_000_000) + "ms");
        System.out.println("Performance improvement: " + (stringTime / sbTime) + "x");
        
        // StringBuilder deve ser significativamente mais rápido
        assertTrue(sbTime < stringTime, "StringBuilder deve ser mais rápido");
        assertTrue(stringTime / sbTime > 2, "StringBuilder deve ser pelo menos 2x mais rápido");
    }
    
    @Test
    @Order(6)
    @DisplayName("Teste de Finalização de Objetos")
    void testObjectFinalization() {
        System.out.println("🔚 Testando finalização de objetos...");
        
        // Cria objetos que serão finalizados
        for (int i = 0; i < 10; i++) {
            new FinalizableObject("Object " + i);
        }
        
        // Força GC e finalização
        System.gc();
        System.runFinalization();
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        // Nota: finalize() é deprecated, mas ainda funciona para demonstração
        System.out.println("✅ Teste de finalização concluído");
    }
    
    @AfterEach
    void tearDown() {
        memoryMonitor.printMemoryInfo("Após teste");
    }
    
    /**
     * Classe para testar finalização (apenas para demonstração)
     */
    private static class FinalizableObject {
        private final String name;
        
        public FinalizableObject(String name) {
            this.name = name;
        }
        
        @Override
        @SuppressWarnings("deprecation")
        protected void finalize() throws Throwable {
            try {
                System.out.println("🗑️  Finalizando: " + name);
            } finally {
                super.finalize();
            }
        }
    }
}


🛠️ Configurações Avançadas de JVM

⚙️ Arquivo de Configuração JVM

Marina criou um arquivo de configuração para diferentes cenários:

# gc-configs.sh - Configurações de GC para diferentes cenários

#!/bin/bash

echo "🚀 Configurações de GC para diferentes cenários"

# =============================================================================
# 1. DESENVOLVIMENTO LOCAL (Máquina do Carlos)
# =============================================================================
echo "💻 Configuração para Desenvolvimento:"
echo "java -XX:+UseG1GC "
echo "     -Xmx1g "
echo "     -Xms512m "
echo "     -XX:+PrintGC "
echo "     -XX:+PrintGCDetails "
echo "     -XX:+UseStringDeduplication "
echo "     -jar gc-demo-project.jar"
echo ""

# =============================================================================
# 2. SERVIDOR DE APLICAÇÃO (Produção)
# =============================================================================
echo "🏭 Configuração para Produção:"
echo "java -XX:+UseG1GC "
echo "     -Xmx8g "
echo "     -Xms4g "
echo "     -XX:MaxGCPauseMillis=200 "
echo "     -XX:+UseStringDeduplication "
echo "     -XX:+PrintGC "
echo "     -XX:+PrintGCDetails "
echo "     -XX:+PrintGCTimeStamps "
echo "     -XX:+PrintGCApplicationStoppedTime "
echo "     -Xloggc:gc.log "
echo "     -XX:+UseGCLogFileRotation "
echo "     -XX:NumberOfGCLogFiles=10 "
echo "     -XX:GCLogFileSize=10M "
echo "     -jar gc-demo-project.jar"
echo ""

# =============================================================================
# 3. APLICAÇÃO DE ALTA FREQUÊNCIA (Trading, Gaming)
# =============================================================================
echo "⚡ Configuração para Baixa Latência:"
echo "java -XX:+UseZGC "
echo "     -Xmx16g "
echo "     -Xms8g "
echo "     -XX:+UseTransparentHugePages "
echo "     -XX:+UnlockExperimentalVMOptions "
echo "     -XX:+UseLargePages "
echo "     -XX:+PrintGC "
echo "     -jar gc-demo-project.jar"
echo ""

# =============================================================================
# 4. PROCESSAMENTO BATCH (Big Data)
# =============================================================================
echo "📊 Configuração para Batch Processing:"
echo "java -XX:+UseParallelGC "
echo "     -Xmx32g "
echo "     -Xms16g "
echo "     -XX:+UseAdaptiveSizePolicy "
echo "     -XX:+PrintGC "
echo "     -XX:+PrintGCDetails "
echo "     -XX:ParallelGCThreads=16 "
echo "     -jar gc-demo-project.jar"
echo ""

# =============================================================================
# 5. MICROSERVIÇOS (Containers)
# =============================================================================
echo "🐳 Configuração para Containers:"
echo "java -XX:+UseContainerSupport "
echo "     -XX:+UseG1GC "
echo "     -XX:MaxRAMPercentage=75.0 "
echo "     -XX:+PrintGC "
echo "     -XX:+ExitOnOutOfMemoryError "
echo "     -jar gc-demo-project.jar"
echo ""

# =============================================================================
# 6. PROFILING E DEBUG
# =============================================================================
echo "🔍 Configuração para Profiling:"
echo "java -XX:+UseG1GC "
echo "     -Xmx2g "
echo "     -Xms1g "
echo "     -XX:+PrintGC "
echo "     -XX:+PrintGCDetails "
echo "     -XX:+PrintGCTimeStamps "
echo "     -XX:+PrintGCApplicationStoppedTime "
echo "     -XX:+PrintStringDeduplicationStatistics "
echo "     -XX:+TraceClassLoading "
echo "     -XX:+TraceClassUnloading "
echo "     -XX:+PrintCompilation "
echo "     -Xloggc:gc-detailed.log "
echo "     -jar gc-demo-project.jar"

🐳 Dockerfile Otimizado

# Dockerfile para aplicação Java com GC otimizado
FROM openjdk:21-jre-slim

# Instala ferramentas de monitoramento
RUN apt-get update && apt-get install -y \
    htop \
    procps \
    && rm -rf /var/lib/apt/lists/*

# Cria usuário não-root
RUN useradd -r -s /bin/false -m -d /app javaapp

# Copia aplicação
COPY target/gc-demo-project-1.0.0.jar /app/app.jar
COPY gc-configs.sh /app/

# Configura permissões
RUN chown -R javaapp:javaapp /app
USER javaapp

# Diretório de trabalho
WORKDIR /app

# Configurações de JVM para container
ENV JAVA_OPTS="-XX:+UseContainerSupport \
               -XX:+UseG1GC \
               -XX:MaxRAMPercentage=75.0 \
               -XX:+PrintGC \
               -XX:+ExitOnOutOfMemoryError \
               -XX:+HeapDumpOnOutOfMemoryError \
               -XX:HeapDumpPath=/app/logs/"

# Cria diretório de logs
RUN mkdir -p /app/logs

# Porta da aplicação
EXPOSE 8080

# Comando de execução
CMD ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

🔧 Script de Monitoramento

#!/bin/bash
# monitor-gc.sh - Script para monitorar GC em tempo real

echo "🔍 Monitor de Garbage Collector - TechSolutions"
echo "Desenvolvido por Marina Santos"
echo "=========================================="

# Função para obter PID do processo Java
get_java_pid() {
    local pid=$(jps -l | grep "gc-demo-project" | awk '{print $1}')
    echo $pid
}

# Função para monitorar GC
monitor_gc() {
    local pid=$1
    
    if [ -z "$pid" ]; then
        echo "❌ Processo Java não encontrado"
        exit 1
    fi
    
    echo "📊 Monitorando processo Java PID: $pid"
    echo "Pressione Ctrl+C para parar"
    echo ""
    
    # Monitoramento contínuo
    while true; do
        echo "⏰ $(date '+%Y-%m-%d %H:%M:%S')"
        
        # Informações de GC
        echo "🗑️  Garbage Collection:"
        jstat -gc $pid | tail -1 | awk '{
            printf "  Eden: %.1f MB, Survivor: %.1f MB, Old: %.1f MB\n", $3/1024, ($4+$5)/1024, $7/1024
            printf "  GC Count: %d Minor + %d Major = %d Total\n", $12, $13, $12+$13
            printf "  GC Time: %.3f s Minor + %.3f s Major = %.3f s Total\n", $14, $15, $14+$15
        }'
        
        # Informações de memória
        echo "💾 Memória:"
        jstat -gccapacity $pid | tail -1 | awk '{
            printf "  Heap Total: %.1f MB, Heap Used: %.1f MB\n", ($1+$2+$3+$4)/1024, 0
        }'
        
        # CPU e threads
        echo "🔄 Threads:"
        jstack $pid | grep -c "^"" | awk '{printf "  Thread Count: %d\n", $1}'
        
        echo "----------------------------------------"
        sleep 5
    done
}

# Função para análise de heap dump
analyze_heap() {
    local pid=$1
    local dump_file="heap-dump-$(date +%Y%m%d-%H%M%S).hprof"
    
    echo "📸 Gerando heap dump..."
    jcmd $pid GC.run_finalization
    jcmd $pid GC.run
    jhsdb jmap --heap --pid $pid
    
    echo "💾 Salvando heap dump em $dump_file"
    jcmd $pid GC.dump_heap $dump_file
    
    echo "✅ Heap dump salvo: $dump_file"
    echo "💡 Analise com: jhat $dump_file"
}

# Função para otimizar GC
optimize_gc() {
    local pid=$1
    
    echo "⚡ Otimizando Garbage Collector..."
    
    # Força GC completo
    jcmd $pid GC.run_finalization
    jcmd $pid GC.run
    
    # Informações após otimização
    echo "📊 Estado após otimização:"
    jstat -gc $pid
    
    echo "✅ Otimização concluída"
}

# Menu principal
show_menu() {
    echo ""
    echo "📋 Menu de Opções:"
    echo "1) Monitorar GC em tempo real"
    echo "2) Gerar heap dump"
    echo "3) Otimizar GC"
    echo "4) Ver estatísticas atuais"
    echo "5) Sair"
    echo ""
    read -p "Escolha uma opção: " choice
    
    local pid=$(get_java_pid)
    
    case $choice in
        1)
            monitor_gc $pid
            ;;
        2)
            analyze_heap $pid
            ;;
        3)
            optimize_gc $pid
            ;;
        4)
            if [ -n "$pid" ]; then
                echo "📊 Estatísticas atuais:"
                jstat -gc $pid
            else
                echo "❌ Processo não encontrado"
            fi
            ;;
        5)
            echo "👋 Até logo!"
            exit 0
            ;;
        *)
            echo "❌ Opção inválida"
            ;;
    esac
}

# Loop principal
while true; do
    show_menu
    echo ""
    read -p "Pressione Enter para continuar..."
done


📊 Comparação de Performance

📈 Benchmarks Detalhados

Marina preparou benchmarks para Carlos comparar os GCs:

package com.techsolutions.benchmark;

import com.techsolutions.model.Product;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Benchmark para comparar performance de diferentes estratégias de GC
 */
public class GCPerformanceBenchmark {
    
    private static final int ITERATIONS = 1000;
    private static final int OBJECTS_PER_ITERATION = 10000;
    
    public static void main(String[] args) {
        System.out.println("🏁 Benchmark de Performance GC");
        System.out.println("============================");
        
        // Aquece a JVM
        warmup();
        
        // Executa benchmarks
        runAllBenchmarks();
        
        // Relatório final
        printFinalReport();
    }
    
    private static void warmup() {
        System.out.println("🔥 Aquecendo JVM...");
        
        for (int i = 0; i < 100; i++) {
            List<Product> products = createProducts(1000);
            products.clear();
        }
        
        System.gc();
        System.out.println("✅ JVM aquecida");
    }
    
    private static void runAllBenchmarks() {
        System.out.println("\n📊 Executando benchmarks...");
        
        // Benchmark 1: Criação massiva de objetos
        benchmarkMassiveObjectCreation();
        
        // Benchmark 2: Coleta de lixo frequente
        benchmarkFrequentGC();
        
        // Benchmark 3: Objetos de vida longa
        benchmarkLongLivedObjects();
        
        // Benchmark 4: Fragmentação de memória
        benchmarkMemoryFragmentation();
    }
    
    private static void benchmarkMassiveObjectCreation() {
        System.out.println("\n🏭 Benchmark: Criação Massiva de Objetos");
        
        long startTime = System.nanoTime();
        long initialMemory = getUsedMemory();
        
        List<List<Product>> allProducts = new ArrayList<>();
        
        for (int i = 0; i < ITERATIONS; i++) {
            List<Product> products = createProducts(OBJECTS_PER_ITERATION);
            allProducts.add(products);
            
            if (i % 100 == 0) {
                System.out.printf("  Iteração %d/%d - Memória: %.1f MB\n", 
                                i, ITERATIONS, getUsedMemory() / (1024.0 * 1024.0));
            }
        }
        
        long endTime = System.nanoTime();
        long finalMemory = getUsedMemory();
        
        System.out.printf("⏱️  Tempo total: %.2f segundos\n", 
                         (endTime - startTime) / 1_000_000_000.0);
        System.out.printf("💾 Memória usada: %.1f MB\n", 
                         (finalMemory - initialMemory) / (1024.0 * 1024.0));
        System.out.printf("📦 Objetos criados: %d\n", 
                         ITERATIONS * OBJECTS_PER_ITERATION);
        
        // Limpa referências
        allProducts.clear();
    }
    
    private static void benchmarkFrequentGC() {
        System.out.println("\n🔄 Benchmark: Coleta de Lixo Frequente");
        
        long startTime = System.nanoTime();
        int gcCount = 0;
        
        for (int i = 0; i < ITERATIONS; i++) {
            // Cria objetos temporários
            List<Product> tempProducts = createProducts(OBJECTS_PER_ITERATION);
            
            // Simula processamento
            processProducts(tempProducts);
            
            // Remove referências (elegível para GC)
            tempProducts.clear();
            tempProducts = null;
            
            // Força GC periodicamente
            if (i % 50 == 0) {
                System.gc();
                gcCount++;
            }
        }
        
        long endTime = System.nanoTime();
        
        System.out.printf("⏱️  Tempo total: %.2f segundos\n", 
                         (endTime - startTime) / 1_000_000_000.0);
        System.out.printf("🗑️  GC forçado: %d vezes\n", gcCount);
    }
    
    private static void benchmarkLongLivedObjects() {
        System.out.println("\n🏠 Benchmark: Objetos de Vida Longa");
        
        long startTime = System.nanoTime();
        
        // Cache de objetos de vida longa
        Map<Long, Product> cache = new HashMap<>();
        
        for (int i = 0; i < ITERATIONS; i++) {
            List<Product> products = createProducts(OBJECTS_PER_ITERATION);
            
            // Alguns objetos vão para o cache (vida longa)
            for (Product product : products) {
                if (product.getId() % 100 == 0) {
                    cache.put(product.getId(), product);
                }
            }
            
            // Limpa objetos temporários
            products.clear();
            
            // Periodicamente limpa parte do cache
            if (i % 200 == 0 && cache.size() > 1000) {
                Iterator<Map.Entry<Long, Product>> iterator = cache.entrySet().iterator();
                int removed = 0;
                while (iterator.hasNext() && removed < 500) {
                    iterator.next();
                    iterator.remove();
                    removed++;
                }
            }
        }
        
        long endTime = System.nanoTime();
        
        System.out.printf("⏱️  Tempo total: %.2f segundos\n", 
                         (endTime - startTime) / 1_000_000_000.0);
        System.out.printf("🏠 Objetos no cache: %d\n", cache.size());
        
        cache.clear();
    }
    
    private static void benchmarkMemoryFragmentation() {
        System.out.println("\n🧩 Benchmark: Fragmentação de Memória");
        
        long startTime = System.nanoTime();
        
        List<Object> mixedObjects = new ArrayList<>();
        Random random = new Random();
        
        for (int i = 0; i < ITERATIONS; i++) {
            // Cria objetos de tamanhos diferentes
            for (int j = 0; j < 1000; j++) {
                if (random.nextBoolean()) {
                    // Objeto pequeno
                    mixedObjects.add(new Product(
                        (long) j, "P" + j, "Desc", 
                        BigDecimal.ONE, "Cat"
                    ));
                } else {
                    // Objeto grande (array)
                    mixedObjects.add(new byte[random.nextInt(10000) + 1000]);
                }
            }
            
            // Remove objetos aleatoriamente (cria fragmentação)
            if (mixedObjects.size() > 50000) {
                for (int k = 0; k < 10000; k++) {
                    int indexToRemove = random.nextInt(mixedObjects.size());
                    mixedObjects.remove(indexToRemove);
                }
            }
        }
        
        long endTime = System.nanoTime();
        
        System.out.printf("⏱️  Tempo total: %.2f segundos\n", 
                         (endTime - startTime) / 1_000_000_000.0);
        System.out.printf("🧩 Objetos restantes: %d\n", mixedObjects.size());
        
        mixedObjects.clear();
    }
    
    private static List<Product> createProducts(int count) {
        List<Product> products = new ArrayList<>();
        Random random = new Random();
        
        for (int i = 0; i < count; i++) {
            Product product = new Product(
                (long) i,
                "Product " + i,
                "Description for product " + i + " with detailed information",
                new BigDecimal(random.nextInt(10000) + 1),
                "Category " + (i % 50)
            );
            products.add(product);
        }
        
        return products;
    }
    
    private static void processProducts(List<Product> products) {
        // Simula processamento
        BigDecimal total = BigDecimal.ZERO;
        for (Product product : products) {
            total = total.add(product.getPrice());
        }
        
        // Evita que o compilador otimize
        if (total.compareTo(BigDecimal.ZERO) < 0) {
            System.out.println("Nunca deve imprimir isso");
        }
    }
    
    private static long getUsedMemory() {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
    
    private static void printFinalReport() {
        System.out.println("\n📋 Relatório Final");
        System.out.println("=================");
        
        Runtime runtime = Runtime.getRuntime();
        
        System.out.println("💻 Informações da JVM:");
        System.out.println("  - Versão: " + System.getProperty("java.version"));
        System.out.println("  - Vendor: " + System.getProperty("java.vendor"));
        System.out.println("  - Processadores: " + runtime.availableProcessors());
        System.out.println("  - Memória máxima: " + 
                          runtime.maxMemory() / (1024 * 1024) + " MB");
        
        // Informações do GC
        System.out.println("\n🗑️  Informações do GC:");
        List<java.lang.management.GarbageCollectorMXBean> gcBeans = 
            java.lang.management.ManagementFactory.getGarbageCollectorMXBeans();
        
        for (java.lang.management.GarbageCollectorMXBean gcBean : gcBeans) {
            System.out.println("  - " + gcBean.getName() + ":");
            System.out.println("    * Coletas: " + gcBean.getCollectionCount());
            System.out.println("    * Tempo total: " + gcBean.getCollectionTime() + " ms");
        }
        
        System.out.println("\n✅ Benchmark concluído!");
    }
}


🎯 Resolução Completa do Problema

🔧 Aplicação Final Otimizada

Marina mostrou para Carlos a versão final otimizada:

package com.techsolutions;

import com.techsolutions.cache.ImprovedProductCache;
import com.techsolutions.model.Product;
import com.techsolutions.service.ProductService;
import com.techsolutions.util.MemoryMonitor;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Aplicação principal otimizada para produção
 * Implementa todas as boas práticas de GC
 */
public class OptimizedECommerceApp {
    
    private final ProductService productService;
    private final MemoryMonitor memoryMonitor;
    private final ScheduledExecutorService scheduler;
    
    public OptimizedECommerceApp() {
        this.productService = new ProductService();
        this.memoryMonitor = new MemoryMonitor();
        this.scheduler = Executors.newScheduledThreadPool(2);
        
        // Configura monitoramento automático
        setupMonitoring();
    }
    
    public static void main(String[] args) {
        System.out.println("🚀 E-Commerce Otimizado - TechSolutions");
        System.out.println("Desenvolvido por Marina Santos & Carlos Silva");
        System.out.println("==========================================");
        
        OptimizedECommerceApp app = new OptimizedECommerceApp();
        
        // Simula operações da aplicação
        app.simulateECommerceOperations();
        
        // Graceful shutdown
        Runtime.getRuntime().addShutdownHook(new Thread(app::shutdown));
    }
    
    private void setupMonitoring() {
        // Monitora memória a cada 30 segundos
        scheduler.scheduleAtFixedRate(() -> {
            if (memoryMonitor.isMemoryUnderPressure()) {
                System.out.println("⚠️  Memória sob pressão, executando limpeza...");
                productService.cleanupCache();
                System.gc(); // Sugestão para GC
            }
        }, 30, 30, TimeUnit.SECONDS);
        
        // Relatório de status a cada 5 minutos
        scheduler.scheduleAtFixedRate(() -> {
            memoryMonitor.printMemoryInfo("Status Report");
            System.out.println(productService.getCacheStatistics());
        }, 5, 5, TimeUnit.MINUTES);
    }
    
    private void simulateECommerceOperations() {
        System.out.println("\n📦 Simulando operações de e-commerce...");
        
        // Simula diferentes cargas de trabalho
        for (int hour = 0; hour < 24; hour++) {
            System.out.println("\n🕐 Hora " + hour + ":00");
            
            // Simula picos de tráfego
            int operationsPerHour = calculateOperationsForHour(hour);
            
            for (int i = 0; i < operationsPerHour; i++) {
                simulateCustomerSession();
                
                // Pequena pausa para simular tráfego real
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            
            // Relatório por hora
            System.out.printf("✅ Hora %d concluída - %d operações\n", 
                             hour, operationsPerHour);
        }
        
        System.out.println("\n🎉 Simulação concluída com sucesso!");
    }
    
    private int calculateOperationsForHour(int hour) {
        // Simula padrão de tráfego realista
        if (hour >= 9 && hour <= 18) {
            return 1000; // Horário comercial
        } else if (hour >= 19 && hour <= 22) {
            return 1500; // Pico noturno
        } else {
            return 200; // Madrugada
        }
    }
    
    private void simulateCustomerSession() {
        try {
            // 1. Cliente busca produtos
            List<Product> products = productService.searchProducts("categoria");
            
            // 2. Cliente visualiza detalhes
            if (!products.isEmpty()) {
                Product product = products.get(0);
                productService.getProductDetails(product.getId());
            }
            
            // 3. Cliente adiciona ao carrinho (simula criação de objeto)
            if (Math.random() > 0.7) {
                productService.addToCart(products.get(0));
            }
            
            // 4. Cliente finaliza compra (simula processamento)
            if (Math.random() > 0.9) {
                productService.processOrder(products);
            }
            
        } catch (Exception e) {
            System.err.println("❌ Erro na sessão do cliente: " + e.getMessage());
        }
    }
    
    private void shutdown() {
        System.out.println("\n🔄 Iniciando shutdown graceful...");
        
        // Para o scheduler
        scheduler.shutdown();
        
        try {
            if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
        
        // Limpa caches
        productService.shutdown();
        
        // Relatório final
        memoryMonitor.printMemoryInfo("Shutdown Final");
        
        System.out.println("✅ Aplicação encerrada com sucesso!");
    }
}

📊 Relatório de Melhoria

Marina preparou um relatório mostrando as melhorias:

package com.techsolutions.report;

import java.util.HashMap;
import java.util.Map;

/**
 * Relatório comparativo das melhorias implementadas
 */
public class ImprovementReport {
    
    public static void main(String[] args) {
        System.out.println("📊 RELATÓRIO DE MELHORIAS - GARBAGE COLLECTOR");
        System.out.println("==============================================");
        
        printPerformanceComparison();
        printMemoryUsageComparison();
        printBestPracticesImplemented();
        printRecommendations();
    }
    
    private static void printPerformanceComparison() {
        System.out.println("\n🏃‍♂️ COMPARAÇÃO DE PERFORMANCE");
        System.out.println("==============================");
        
        Map<String, Double> beforeOptimization = new HashMap<>();
        beforeOptimization.put("Tempo de resposta médio", 10.5);
        beforeOptimization.put("Uso de memória pico", 85.0);
        beforeOptimization.put("Pausas GC médias", 250.0);
        beforeOptimization.put("Throughput", 1000.0);
        
        Map<String, Double> afterOptimization = new HashMap<>();
        afterOptimization.put("Tempo de resposta médio", 2.1);
        afterOptimization.put("Uso de memória pico", 45.0);
        afterOptimization.put("Pausas GC médias", 15.0);
        afterOptimization.put("Throughput", 4500.0);
        
        System.out.println("ANTES vs DEPOIS das otimizações:");
        System.out.println("┌────────────────────────────┬──────────┬──────────┬───────────┐");
        System.out.println("│ Métrica                    │ Antes    │ Depois   │ Melhoria  │");
        System.out.println("├────────────────────────────┼──────────┼──────────┼───────────┤");
        
        for (String metric : beforeOptimization.keySet()) {
            double before = beforeOptimization.get(metric);
            double after = afterOptimization.get(metric);
            double improvement = calculateImprovement(metric, before, after);
            
            System.out.printf("│ %-26s │ %8.1f │ %8.1f │ %8.1f%% │\n", 
                             metric, before, after, improvement);
        }
        
        System.out.println("└────────────────────────────┴──────────┴──────────┴───────────┘");
    }
    
    private static double calculateImprovement(String metric, double before, double after) {
        if (metric.equals("Throughput")) {
            return ((after - before) / before) * 100;
        } else {
            return ((before - after) / before) * 100;
        }
    }
    
    private static void printMemoryUsageComparison() {
        System.out.println("\n💾 COMPARAÇÃO DE USO DE MEMÓRIA");
        System.out.println("===============================");
        
        System.out.println("ANTES (Cache Problemático):");
        System.out.println("  🔴 Vazamento: Objetos acumulavam indefinidamente");
        System.out.println("  🔴 Fragmentação: Memória fragmentada");
        System.out.println("  🔴 GC Frequente: Coletas constantes e longas");
        System.out.println("  🔴 OutOfMemory: Erros frequentes");
        
        System.out.println("\nDEPOIS (Cache Otimizado):");
        System.out.println("  ✅ Controle: SoftReference permite GC automático");
        System.out.println("  ✅ Compactação: Memória bem organizada");
        System.out.println("  ✅ GC Otimizado: Coletas rápidas e eficientes");
        System.out.println("  ✅ Estabilidade: Sem erros de memória");
    }
    
    private static void printBestPracticesImplemented() {
        System.out.println("\n✅ MELHORES PRÁTICAS IMPLEMENTADAS");
        System.out.println("===================================");
        
        String[] practices = {
            "Uso de SoftReference para caches",
            "Limpeza proativa de referências",
            "Monitoramento automático de memória",
            "Configuração otimizada do G1GC",
            "Pool de objetos para reduzir criação",
            "StringBuilder em vez de concatenação String",
            "Lazy initialization para objetos grandes",
            "WeakReference para listeners",
            "Finalização adequada de recursos",
            "Logging estruturado de GC"
        };
        
        for (int i = 0; i < practices.length; i++) {
            System.out.printf("%2d. ✅ %s\n", i + 1, practices[i]);
        }
    }
    
    private static void printRecommendations() {
        System.out.println("\n💡 RECOMENDAÇÕES PARA PRODUÇÃO");
        System.out.println("===============================");
        
        System.out.println("🔧 CONFIGURAÇÃO DE JVM:");
        System.out.println("  • Use G1GC para aplicações > 4GB heap");
        System.out.println("  • Configure MaxGCPauseMillis=200ms");
        System.out.println("  • Ative UseStringDeduplication");
        System.out.println("  • Configure logs de GC rotativos");
        
        System.out.println("\n📊 MONITORAMENTO:");
        System.out.println("  • APM: Application Performance Monitoring");
        System.out.println("  • Métricas: Micrometer + Prometheus");
        System.out.println("  • Alertas: Uso de memória > 80%");
        System.out.println("  • Dashboards: Grafana para visualização");
        
        System.out.println("\n🧪 TESTES:");
        System.out.println("  • Load Testing: Simular picos de tráfego");
        System.out.println("  • Memory Leak Testing: Detectar vazamentos");
        System.out.println("  • GC Tuning: Ajustar parâmetros");
        System.out.println("  • Profiling: Análise regular com ferramentas");
        
        System.out.println("\n🚀 PRÓXIMOS PASSOS:");
        System.out.println("  1. Implementar em ambiente de teste");
        System.out.println("  2. Executar testes de carga");
        System.out.println("  3. Configurar monitoramento");
        System.out.println("  4. Deploy gradual em produção");
        System.out.println("  5. Monitorar e ajustar conforme necessário");
    }
}


🎓 Conclusão e Aprendizados

📚 Resumo dos Conceitos

Marina fez um resumo final para Carlos:

“Carlos, agora você domina os conceitos fundamentais do Garbage Collector. Lembre-se sempre destes pontos principais:”

🎯 Pontos-Chave Aprendidos

  1. 🧠 Entendimento do GC: Como funciona internamente e os diferentes algoritmos
  2. 🔍 Identificação de Problemas: Como detectar vazamentos e gargalos
  3. 🛠️ Otimização de Código: Práticas para reduzir pressão no GC
  4. 📊 Monitoramento: Ferramentas e técnicas para acompanhar performance
  5. ⚙️ Configuração: Parâmetros de JVM para diferentes cenários

🏆 Resultados Alcançados

  • 80% redução no tempo de resposta
  • 60% redução no uso de memória
  • 95% redução nas pausas de GC
  • 350% aumento no throughput
  • Zero erros de OutOfMemory

Carlos“Marina, muito obrigado! Agora me sinto confiante para trabalhar com aplicações Java em produção. Vou aplicar todos esses conceitos nos nossos projetos!”

Marina“Perfeito, Carlos! Lembre-se: o GC é seu aliado, não seu inimigo. Compreendê-lo é fundamental para ser um desenvolvedor Java experiente. Continue estudando e praticando!”

🎉 Projeto Concluído com Sucesso!

A aplicação de e-commerce da TechSolutions agora roda de forma estável e eficiente, atendendo milhares de usuários sem problemas de memória. A equipe está preparada para enfrentar qualquer desafio relacionado ao Garbage Collector!


💡 Dica Final: Este projeto pode ser estendido com funcionalidades como métricas em tempo real, integração com APM tools, testes automatizados de performance, e muito mais. O importante é continuar aprendendo e praticando!

Rolar para cima