1. Introdução
O objeto Java contém:
- campos declarados na superclasse?
- campos privados declarados em uma superclasse?
- métodos?
- elementos da matriz?
- comprimento da matriz?
- outro objeto (em si)?
- código hash?
- tipo (próprio)?
- nome (próprio)?
As respostas para essas (e outras) perguntas podem ser obtidas usando 
a biblioteca de classes org.openjdk.jol , que, em particular, permite entender que o objeto é uma área de memória:
- contendo:- cabeçalho (até 16 bytes) e nele:
 - código hash
- tipo de referência
- comprimento da matriz (para matriz)
 
- todos os campos (incluindo particulares) declarados em todas as superclasses
- ou elementos da matriz (para uma matriz)
 
- não contém:
 - variáveis estáticas
- métodos
- outros objetos em si mesmo
- próprio nome (ou seja, o objeto não tem um nome)
 
 
Preparação
Aqui estão os resultados da avaliação da memória de objetos de vários tipos pelo método a partir da descrição do 
pacote java.lang.instrument (veja também 
aqui ). Esses resultados nos permitem responder à maioria das perguntas colocadas acima.
As etapas a seguir devem ser concluídas:
- Crie uma classe de agente contendo o método premain:
 public static void premain(String, Instrumentation) {...}
 
- Crie um archive contendo a classe do agente e o arquivo de manifesto com o conteúdo:
  Premain-class: --
 
- Crie uma classe executável para avaliar a memória.
- Especifique o archive com o parâmetro "-javaagent" ao iniciar a máquina virtual:
  java -javaagent:- --
 
 
Vamos começar com um caso de teste. Para simplificar, usamos um pacote sem nome.
Etapa 1. Crie uma classe de agente de avaliação
 import java.lang.instrument.Instrumentation; public class A { public static void premain(String notUsedHere, Instrumentation i) { System.out.println("premain"); } } 
Nós compilamos:
 javac A.java 
Etapa 2. Crie um arquivo de manifesto m.txt contendo:
 Premain-class: A   
ATENÇÃO: a segunda linha do arquivo deve ser VAZIA, NÃO CONTENDO ESPAÇOS.Crie o arquivo A.jar:
 jar cmf m.txt A.jar A.class 
Etapa 3. Crie uma classe executável de avaliação
 public class M { public static void main(String[] notUsedHere) {  
Nós compilamos:
 javac M.java 
Etapa 4. Execute
 java -javaagent:A.jar M 
Resultado:
 premain main 
indica que o método premain da classe do agente foi chamado primeiro e, em seguida, o método principal da classe executável.
Agora crie a classe de agente necessária:
 import java.lang.instrument.Instrumentation; public class A {  
e classe executável:
 class M { public static void main(String[] notUsedHere) { mem("Object", new Object()); } private static void mem(Object o, Object ref) { System.out.println(o + ": " + objectBytesEstimate(ref)); } private static long objectBytesEstimate(Object ref) { if (A.instrumentation() == null) { throw new RuntimeException("Not initialized instrumentation."); } return A.instrumentation().getObjectSize(ref); } } 
Método
 long getObjectSize(Object --) 
retorna uma ESTIMAÇÃO do tamanho (número de bytes) de memória ocupada pelo objeto no link especificado. Deve-se ter em mente que a estimativa resultante pode ser diferente para outra máquina virtual. Os valores para 
jdk-13 serão listados 
aqui .
Realizamos:
 javac *.java jar cmf m.txt A.jar A.class java -javaagent:A.jar M 
e obtemos o resultado:
 Object: 16 
mostrando que um objeto VAZIO do tipo Objeto ocupa aqui (POR AVALIAÇÃO) 16 bytes. Desses, 12 bytes ocupam o cabeçalho e 4 bytes no final servem para alinhar o comprimento do objeto ao limite de 8 bytes.
Resultados
Outros exemplos conterão apenas o código colocado no método principal da classe M. Eles devem ser executados para cada exemplo com os comandos:
 javac M.java java -javaagent:A.jar M 
Não é necessário recriar o A.jar.
Por exemplo, para obter uma estimativa do tamanho da memória de um objeto de um tipo arbitrário sem campos, colocamos o código no método principal:
 class C {}; mem("Empty", new C());  
O resultado indicado no comentário mostra que um objeto sem campos ocupa tantos bytes quanto um objeto do tipo Object.
Além disso, o resultado do programa:
 {class C {int a; } mem(1, new C());}  
indica que cada campo int ocupa 4 bytes. Observo que aqui cada linha é um bloco separado, o que permite que você use o mesmo nome para diferentes classes.
Cada campo longo é de 8 bytes:
 {class C {long a; } mem(1, new C());}  
Cada campo booleano recebe 1 byte (para esta VM):
 {class C {boolean a; } mem(1, new C());}  
Cada campo de referência ocupa 4 bytes (para esta VM):
 {class C {Boolean a; } mem(1, new C());}  
Um campo do tipo String também possui 4 bytes, como todas as referências:
 {class C {String a; } mem(" null", new C());}  
Um campo de referência de matriz também ocupa 4 bytes, como todo referência:
 {class C {int[] a; } mem("null", new C());}  
O objeto subtipo contém cada campo declarado na superclasse, independentemente do modificador de acesso:
 {class S { } class C extends S {long a;} mem("0+1", new C());}  
O objeto subtipo contém um campo declarado na superclasse com o mesmo nome da subclasse (o chamado hidden - hidden):
 {class S { } class C extends S {long a,b;} mem("0+2", new C());}  
Um objeto de subtipo contém cada campo declarado em cada uma de suas superclasses:
 class U {private long a; } class S extends U {private long a; } class C extends S { long a; } mem("1+1+1", new C());  
Vire para matrizes. Como você sabe, uma matriz é um tipo especial de objeto cujos elementos estão no próprio objeto; portanto, o tamanho da memória ocupada pela matriz aumenta com o número de elementos:
 {long[] a = new long[ 0]; mem(" 0", a);}  
E para a variedade de links:
 {Long[] a = new Long[ 0]; mem(" 0", a);}  
Agora, por curiosidade, comparamos os tamanhos de vários objetos de diferentes tipos:
 mem(" Object", new Object());  
O mesmo para o jdk diferente em um processador de 64 bits:
                 jdk1.6.0_45 jdk1.7.0_80 jdk1.8.0_191 jdk-9 jdk-12 jdk-13
                 ----------- ----------- ------------ ------ ------ ---- -
       Objeto: 16 16 16 16 16 16
       String: 32 24 24 24 24 24
    Exceção: 32 32 32 40 40 40
    int.class: 104 88 104 112 104 112
  int []. classe: 584 544 480 112 104 112
 Classe de objeto: 600 560 496 112 104 112
 System.class: 624 560 496 144 152 160
 String.class: 696 640 624 136 128 136 A estimativa do tamanho da String é de 24 bytes, embora a classe String contenha muitas variáveis estáticas, métodos estáticos e não estáticos. Indubitavelmente, isso indica a ausência de variáveis estáticas e código do método no objeto. O mesmo vale para um objeto de qualquer tipo.
Concluindo, um lembrete: todos os dados sobre o tamanho do objeto são estimados e podem variar em certa medida de uma execução para outra e, é claro, para diferentes máquinas virtuais.