JVM, Java Memory Model e CPU: por que funciona em x86 e quebra em ARM
Source: Dev.to
Introdução
“Mas nunca deu problema na minha máquina.”
Provavelmente porque sua máquina é x86. Troque para ARM e alguns bugs de concorrência que estavam “dormindo” aparecem.
A ideia central: x86 costuma ser mais “conservador” na prática, enquanto ARM permite mais reordenações. Se o seu código depende de comportamento “bonzinho” do hardware, ele pode sobreviver em x86 e falhar em ARM.
Java Memory Model (JMM)
O JMM não diz apenas “threads compartilham memória e pronto”. Ele define:
- Visibilidade – quando o que uma thread escreveu passa a ser visto por outra.
- Ordem – quais reordenações são permitidas.
- Happens‑before – a relação que cria garantias reais entre threads.
Sem um happens‑before entre duas ações, não há garantia de:
- ver o valor mais recente;
- ver as coisas na ordem que foram escritas;
- ver um objeto “pronto”.
Esse é o motivo de muitos bugs “fantasma”.
Exemplo clássico
x = 42;
ready = true;
Muitos imaginam que, ao ver ready == true, outra thread necessariamente verá x == 42. O JMM permite que, sem sincronização, a outra thread observe:
ready == true
x == 0
Por quê?
xpode ficar em cache/registrador;- as escritas podem ser reordenadas;
- a outra thread pode ler valores antigos.
Esse é o bug mais traiçoeiro.
Publicação segura de objetos
instance = new MyObject();
Embora pareça uma única operação, por baixo acontece algo como:
- alocar memória;
- inicializar campos (construtor);
- publicar a referência (
instanceaponta para o objeto).
Sem sincronização, a JVM/CPU podem efetivamente permitir que a publicação (3) aconteça antes da inicialização (2) ser visível para outras threads. Assim, uma segunda thread pode executar:
if (instance != null) {
instance.doSomething();
}
instance não é null, mas o objeto pode ainda estar com campos em estado “default” (0/null). Em padrões como double‑checked locking isso já causou problemas reais em produção.
Uso de volatile
private static volatile MyObject instance;
Declarar a referência como volatile traz duas garantias importantes:
- Visibilidade – a leitura de
volatilevê o valor mais recente (não “preso” em cache local). - Ordem –
volatilecria barreiras que impedem certas reordenações ao redor da variável.
Uma escrita em um volatile happens‑before de qualquer leitura posterior do mesmo volatile. Na prática, se a thread B leu instance (volatile) como não‑nulo, ela tem garantia de enxergar todas as escritas que aconteceram antes da thread A publicar a referência (incluindo os writes do construtor que “prepararam” o objeto).
Como o JIT trata volatile
Escrita em volatile
Ao compilar uma escrita em volatile, a JVM deve garantir que:
- todas as escritas anteriores não fiquem “penduradas” e invisíveis;
- a publicação do valor não “passe na frente” de operações anteriores.
Isso é implementado inserindo memory fences/barriers ao redor do acesso (o tipo exato depende da arquitetura e do JIT).
Leitura em volatile
Ao compilar uma leitura em volatile, a JVM deve garantir que:
- você não leia um valor velho do cache/registrador;
- leituras subsequentes não sejam movidas “para antes” dessa leitura.
Também são usadas barreiras e instruções com semântica apropriada.
x86 vs. ARM
Em x86, muitas reordenações são menos agressivas e a plataforma tende a “ajudar” sem que você peça. Isso não significa que o código está correto — apenas que o bug pode não se manifestar.
Em ARM, há mais reordenações permitidas e a arquitetura exige sincronização explícita para garantir ordem e visibilidade. Se você não usou volatile, synchronized, locks ou atomics, ARM tem maior chance de expor o bug.
Consequência: o mesmo programa “ok” em x86 pode falhar em ARM sob carga.
Regra prática (sem filosofia)
Se há compartilhamento entre threads, escolha uma estratégia adequada:
| Estratégia | Quando usar |
|---|---|
volatile | Flags/estado simples e publicação segura de referência |
synchronized / Lock | Invariantes e operações compostas |
Atomic* | Operações atômicas sem lock (CAS), com custos/limites próprios |
Se você não tem um happens‑before explícito, está apostando na arquitetura e no acaso.