0Pricing
Java Academy · Lezione

Individuare i colli di bottiglia

Misurare prima di ottimizzare

Individuare i colli di bottiglia è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.

Misuri, non vada a intuito

La prima regola per migliorare le prestazioni è: misuri prima di ottimizzare.

L'intuizione su dove un programma Java trascorra il proprio tempo è solitamente errata. Il compilatore JIT, il garbage collector e il caching rendono inaffidabili le supposizioni vaghe. Esegua il profiling, individui il vero punto caldo e intervenga su quello.

Definizione di collo di bottiglia

Un collo di bottiglia è la parte del sistema che limita il throughput complessivo o la latenza.

Ottimizzare qualsiasi altra parte non produce vantaggi visibili. La legge di Amdahl lo rende preciso: se il 90% del tempo viene trascorso in un metodo, velocizzare il restante 10% non può mai portare a un miglioramento superiore all'11%.

Latenza e throughput

Decida che cosa sta ottimizzando:

  • Latenza — quanto tempo richiede una singola richiesta.
  • Throughput — quante richieste vengono gestite al secondo.

Esiste un compromesso tra i due. L'elaborazione in batch migliora il throughput, ma può aumentare la latenza per richiesta. Definisca l'obiettivo prima di procedere con il tuning.

Misurare il tempo trascorso

La misurazione più rudimentale consiste nel misurare il tempo reale prima e dopo un blocco di codice con System.nanoTime().

È utile per un rapido controllo di plausibilità, ma include il warmup del JIT, le pause del GC e il rumore dovuto alla pianificazione del sistema operativo; pertanto, consideri con cautela i valori singoli.

public class Main {
    public static void main(String[] args) {
        long start = System.nanoTime();
        long sum = 0;
        for (int i = 0; i < 10_000_000; i++) sum += i;
        long elapsed = System.nanoTime() - start;
        System.out.println("Sum: " + sum);
        System.out.println("Elapsed ms: " + (elapsed / 1_000_000.0));
    }
}

Attenzione al warmup del JIT

Java inizia interpretando il bytecode, quindi il JIT compila i metodi più utilizzati in codice nativo.

Per questo le prime esecuzioni di un metodo sono molto più lente di quelle successive. Un ciclo di misurazione ingenuo misura soprattutto il warmup. I benchmark reali eseguono prima il warmup e poi misurano lo stato stazionario: è esattamente ciò che JMH fa automaticamente.

CPU-bound e I/O-bound

Classifichi il collo di bottiglia:

  • CPU-bound — i thread sono impegnati nei calcoli e i core sono saturi.
  • I/O-bound — i thread attendono il disco, la rete o il database.

I profiler distinguono questi casi come tempo 'on-CPU' rispetto a tempo 'bloccato/in attesa'. La soluzione è completamente diversa: algoritmi più veloci da una parte, maggiore concorrenza o meno round trip dall'altra.

Campionamento e strumentazione

Esistono due strategie di profiling:

  • Campionamento — acquisisce periodicamente gli stack trace. Ha un overhead ridotto ed è di natura statistica.
  • Strumentazione — inserisce contatori in ogni metodo. È precisa, ma pesante e può alterare i tempi misurati.

In produzione, preferisca il campionamento a basso overhead, come quello di Java Flight Recorder.

La memoria come collo di bottiglia

Spesso il costo reale è dovuto alle allocazioni, non ai calcoli. La creazione eccessiva di oggetti attiva raccolte frequenti da parte del garbage collector, sottraendo CPU e aggiungendo pause.

Controlli il tasso di allocazione e il tempo trascorso nel GC. Ridurre le allocazioni in un ciclo molto utilizzato spesso è più efficace di ottimizzare minuziosamente le operazioni aritmetiche.

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // Allocation-heavy: a new String each iteration
        List<String> garbage = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            garbage.add("item-" + i);
        }
        System.out.println("Allocated " + garbage.size() + " strings");
        System.out.println("In a hot loop, this churn drives GC pressure");
    }
}

Individuare la cima dello stack

Un profiler basato sul campionamento produce un elenco di metodi ordinati in base alla frequenza con cui sono comparsi in uno stack della CPU: il self time.

Il metodo in cima è il candidato da esaminare. Verifichi però che si trovi sul percorso critico: un metodo molto utilizzato eseguito da un logger in background potrebbe non influire sulla latenza percepita dall'utente.

Stabilire una baseline

Prima di modificare qualsiasi cosa, registri una misurazione di baseline in condizioni di carico realistiche.

Dopo ogni modifica, misuri nuovamente e confronti i risultati. Senza una baseline non può dimostrare che un'ottimizzazione abbia funzionato e molte presunte «ottimizzazioni» peggiorano le prestazioni. Modifichi una sola cosa alla volta.

Profilare sotto un carico realistico

Un collo di bottiglia individuato su un laptop inattivo potrebbe non essere quello che causa problemi in produzione.

  • Utilizzi dimensioni dei dati e livelli di concorrenza rappresentativi.
  • Riproduca il carico di lavoro che conta davvero per gli utenti.

I micro-test sintetici possono indicare un metodo irrilevante su larga scala. Esegua il profiling dove si manifesta davvero il problema.

Verifica rapida

Perché le misurazioni singole e ingenue di System.nanoTime() di un metodo Java sono spesso fuorvianti?

Riepilogo

Individuare i colli di bottiglia con metodo:

  • Misurare prima di ottimizzare: l'intuizione inganna.
  • Scegliere un obiettivo: latenza o throughput.
  • Distinguere tra CPU-bound e I/O-bound; osservare GC e allocazioni.
  • Preferire profiler a campionamento con overhead ridotto.
  • Fare attenzione al warmup del JIT; stabilire una baseline e modificare una sola cosa alla volta.

Domande Frequenti

La lezione «Individuare i colli di bottiglia» è gratuita?

Sì — il testo completo di «Individuare i colli di bottiglia» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.

Cosa imparerò in «Individuare i colli di bottiglia»?

Misurare prima di ottimizzare Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Java Academy?

Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Individuare i colli di bottiglia»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Java Academy?

Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Individuare i colli di bottiglia
  2. Java Flight Recorder
  3. Analizzare con JDK Mission Control
  4. Flag comuni per l'ottimizzazione della JVM
← Torna a Java Academy