Eu tenho um aplicativo que lê um arquivo CSV com pilhas de linhas de dados. Dou ao usuário um resumo do número de linhas com base nos tipos de dados, mas quero garantir que não leia muitas linhas de dados e cause OutOfMemoryError
s. Cada linha se traduz em um objeto. Existe uma maneira fácil de descobrir o tamanho desse objeto programaticamente? Existe uma referência que define como são grandes os tipos primitivos e as referências a objetos VM
?
No momento, tenho um código que diz ler até 32.000 linhas , mas também gostaria de ter um código que leia o maior número possível de linhas até que eu usei 32 MB de memória. Talvez essa seja uma pergunta diferente, mas eu ainda gostaria de saber.
Respostas:
Você pode usar o pacote java.lang.instrument
Compile e coloque esta classe em um JAR:
Adicione o seguinte ao seu
MANIFEST.MF
:Use getObjectSize:
Invocar com:
fonte
byte[0]
,byte[1]
,byte[5]
,int[0]
,int[1]
,int[2]
usando a abordagem que você descreveu? Seria bom se os resultados incluíssem sobrecarga no comprimento da matriz e no alinhamento da memória.Você deve usar o jol , uma ferramenta desenvolvida como parte do projeto OpenJDK.
Para obter os tamanhos de primitivas, referências e elementos de matriz, use
VMSupport.vmDetails()
. No Oracle JDK 1.8.0_40 em execução no Windows de 64 bits (usado para todos os exemplos a seguir), esse método retornaVocê pode obter o tamanho superficial de uma instância de objeto usando
ClassLayout.parseClass(Foo.class).toPrintable()
(opcionalmente passando uma instância paratoPrintable
). Este é apenas o espaço consumido por uma única instância dessa classe; não inclui nenhum outro objeto referenciado por essa classe. Ele não incluem VM sobrecarga para o cabeçalho objeto, o alinhamento campo e estofamento. Parajava.util.regex.Pattern
:Você pode obter uma visão resumida do tamanho profundo de uma instância de objeto usando
GraphLayout.parseInstance(obj).toFootprint()
. Obviamente, alguns objetos na área de cobertura podem ser compartilhados (também referenciados em outros objetos), portanto, é uma supervalorização do espaço que pode ser recuperado quando esse objeto é coletado de lixo. Para o resultado dePattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
(extraído desta resposta ), jol relata uma área total de 1840 bytes, dos quais apenas 72 são a própria instância Pattern.Se você usar
GraphLayout.parseInstance(obj).toPrintable()
, o jol informará o endereço, tamanho, tipo, valor e caminho das referências a campo para cada objeto referenciado, embora geralmente sejam muitos detalhes para serem úteis. Para o exemplo de padrão em andamento, você pode obter o seguinte. (Os endereços provavelmente mudarão entre as execuções.)As entradas "(algo mais)" descrevem outros objetos no heap que não fazem parte desse gráfico de objetos .
A melhor documentação para jol são as amostras de jol no repositório de jol. As amostras demonstram operações comuns de jol e mostram como você pode usar o jol para analisar as VMs e os coletores de lixo.
fonte
vmDetails
é agoraVM.current().details()
.GraphLayout.parseInstance(instance).toFootprint()
que achei mais útil entender o tamanho dos objetosAchei acidentalmente uma classe java "jdk.nashorn.internal.ir.debug.ObjectSizeCalculator", já em jdk, que é fácil de usar e parece bastante útil para determinar o tamanho de um objeto.
resultados:
fonte
ObjectSizeCalculator
é suportado apenas no HotSpot VMAlguns anos atrás, o Javaworld publicou um artigo sobre como determinar o tamanho de objetos Java compostos e potencialmente aninhados . Eles basicamente criam uma implementação sizeof () em Java. A abordagem baseia-se basicamente em outro trabalho em que as pessoas identificaram experimentalmente o tamanho de objetos primitivos e típicos de Java e aplicaram esse conhecimento a um método que percorre recursivamente um gráfico de objetos para calcular o tamanho total.
Sempre será um pouco menos preciso do que uma implementação nativa de C simplesmente por causa das coisas acontecendo nos bastidores de uma classe, mas deve ser um bom indicador.
Como alternativa, um projeto SourceForge chamado apropriadamente sizeof que oferece uma biblioteca Java5 com uma implementação sizeof ().
PS Não use a abordagem de serialização, não há correlação entre o tamanho de um objeto serializado e a quantidade de memória que ele consome ao vivo.
fonte
Primeiro, "o tamanho de um objeto" não é um conceito bem definido em Java. Você pode significar o próprio objeto, apenas com seus membros, o Objeto e todos os objetos aos quais se refere (o gráfico de referência). Você pode significar o tamanho da memória ou o tamanho do disco. E a JVM pode otimizar coisas como Strings.
Portanto, a única maneira correta é perguntar à JVM, com um bom perfilador (eu uso YourKit ), o que provavelmente não é o que você deseja.
No entanto, a partir da descrição acima, parece que cada linha será independente e não terá uma grande árvore de dependência; portanto, o método de serialização provavelmente será uma boa aproximação na maioria das JVMs. A maneira mais fácil de fazer isso é a seguinte:
Lembre-se de que se você tiver objetos com referências comuns, isso não dará o resultado correto e o tamanho da serialização nem sempre corresponderá ao tamanho da memória, mas é uma boa aproximação. O código será um pouco mais eficiente se você inicializar o tamanho ByteArrayOutputStream para um valor razoável.
fonte
Se você gostaria de saber quanta memória está sendo usada na sua JVM e quanto é livre, tente algo como isto:
edit: Eu pensei que isso poderia ser útil, pois o autor da pergunta também afirmou que gostaria de ter uma lógica que lide com "leia o maior número possível de linhas até que eu usei 32 MB de memória".
fonte
Quando trabalhei no Twitter, escrevi um utilitário para calcular o tamanho profundo do objeto. Ele leva em conta diferentes modelos de memória (32 bits, oops compactados, 64 bits), preenchimento, preenchimento de subclasse, funciona corretamente em estruturas de dados circulares e matrizes. Você pode apenas compilar este arquivo .java; não possui dependências externas:
https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java
fonte
Muitas das outras respostas fornecem tamanhos rasos - por exemplo, o tamanho de um HashMap sem nenhuma das chaves ou valores, o que provavelmente não é o que você deseja.
O projeto jamm usa o pacote java.lang.instrumentation acima, mas percorre a árvore e, portanto, pode fornecer o uso profundo da memória.
https://github.com/jbellis/jamm
fonte
Você tem que andar pelos objetos usando reflexão. Tenha cuidado como você faz:
byte
é teoricamente 1 byte não significa que leva apenas um na memória.HashMap
ou mais itens iguais a object como o comparador para eliminar loops infinitos.@ jodonnell: Eu gosto da simplicidade da sua solução, mas muitos objetos não são serializáveis (portanto, isso geraria uma exceção), os campos podem ser transitórios e os objetos podem substituir os métodos padrão.
fonte
Você deve medir com uma ferramenta ou estimar manualmente, e isso depende da JVM que você está usando.
Há alguma sobrecarga fixa por objeto. É específico da JVM, mas eu costumo estimar 40 bytes. Então você tem que olhar para os membros da classe. As referências de objeto são 4 (8) bytes em uma JVM de 32 bits (64 bits). Os tipos primitivos são:
Matrizes seguem as mesmas regras; ou seja, é uma referência a objeto, que ocupa 4 (ou 8) bytes no objeto e, em seguida, seu comprimento multiplicado pelo tamanho do elemento.
Tentar fazer isso de forma programática com chamadas
Runtime.freeMemory()
não oferece muita precisão, devido a chamadas assíncronas para o coletor de lixo, etc. Criar um perfil do heap com -Xrunhprof ou outras ferramentas fornecerá os resultados mais precisos.fonte
boolean[]
. Na verdade, todos os tipos primitivos, não duplos / longos, têm 4 bytes. Os últimos são 8 (a resposta errada coloca-los como 4 também)A
java.lang.instrument.Instrumentation
classe fornece uma ótima maneira de obter o tamanho de um objeto Java, mas requer que você defina umpremain
execute seu programa com um agente java. Isso é muito chato quando você não precisa de nenhum agente e precisa fornecer um agente Jar fictício para o seu aplicativo.Então, eu tenho uma solução alternativa usando a
Unsafe
classe dosun.misc
. Portanto, considerando o alinhamento da pilha de objetos de acordo com a arquitetura do processador e calculando o deslocamento máximo do campo, é possível medir o tamanho de um Objeto Java. No exemplo abaixo, eu uso uma classe auxiliarUtilUnsafe
para obter uma referência aosun.misc.Unsafe
objeto.fonte
Há também a ferramenta Memory Measurer (anteriormente no Google Code , agora no GitHub ), que é simples e publicada sob a licença Apache 2.0 comercial , conforme discutida em uma pergunta semelhante .
Também requer um argumento de linha de comando para o interpretador java, se você deseja medir o consumo de bytes de memória, mas parece funcionar bem, pelo menos nos cenários que eu o usei.
fonte
Sem precisar mexer na instrumentação e assim por diante, e se você não precisar saber o tamanho exato de bytes de um objeto, poderá seguir a seguinte abordagem:
Dessa forma, você lê a memória usada antes e depois e, chamando o GC imediatamente antes de obter a memória usada, diminui o "ruído" quase para 0.
Para obter um resultado mais confiável, você pode executar seu trabalho n vezes e, em seguida, dividir a memória usada por n, obtendo quanta memória uma execução leva. Ainda mais, você pode executar a coisa toda mais vezes e fazer uma média.
fonte
System.gc()
apenas notifica que você deseja GC? Não é garantido que o GC seja chamado.Aqui está um utilitário que eu criei usando alguns dos exemplos vinculados para lidar com 32 bits, 64 bits e 64 bits com OOP compactado. Ele usa
sun.misc.Unsafe
.Ele é usado
Unsafe.addressSize()
para obter o tamanho de um ponteiro nativo eUnsafe.arrayIndexScale( Object[].class )
o tamanho de uma referência Java.Ele usa o deslocamento de campo de uma classe conhecida para calcular o tamanho base de um objeto.
fonte
Instrumentation
porque não inicio o tomcat,ObjectSizeCalculator
porque não tenho certeza do tipo de VM (HotSpot) e doJOL
feijão de bacouse. Eu uso isso e adicione segundo parâmetro para ignorar singletons vizAbstractRefreshableApplicationContext.getBeanFactory().getSingletonMutex()
e refatorarinternalSizeOf
código para ignorar Classe e EnumEu estava procurando por um cálculo em tempo de execução de um tamanho de objeto que atendesse aos seguintes requisitos:
O seguinte é baseado no código principal do artigo original de especialistas em java ( https://www.javaspecialists.eu/archive/Issue078.html ) e em alguns bits da versão insegura em outra resposta a esta pergunta.
Espero que alguém ache útil.
}
fonte
Não há uma chamada de método, se é isso que você está pedindo. Com um pouco de pesquisa, suponho que você possa escrever sua própria. Uma instância específica possui um tamanho fixo derivado do número de referências e valores primitivos mais os dados da contabilidade da instância. Você simplesmente percorreria o gráfico do objeto. Quanto menos variados os tipos de linha, mais fácil.
Se isso for muito lento ou apenas mais problemas do que vale, sempre haverá boas linhas antiquadas contando a regra de ouro.
fonte
Eu escrevi um teste rápido uma vez para estimar rapidamente:
O conceito geral é alocar objetos e medir a mudança no espaço livre da pilha. A chave é
getFreeMemory()
que solicita que o GC seja executado e aguarda a estabilização do tamanho do heap livre relatado . A saída do acima é:É o que esperamos, dado o comportamento do alinhamento e a possível sobrecarga do cabeçalho do bloco de heap.
O método de instrumentação detalhado na resposta aceita aqui é o mais preciso. O método que descrevi é preciso, mas apenas sob condições controladas em que nenhum outro encadeamento está criando / descartando objetos.
fonte
Basta usar java visual VM.
Tem tudo o que você precisa para analisar e depurar problemas de memória.
Ele também possui um console OQL (Object Query Language) que permite fazer muitas coisas úteis, uma das quais
sizeof(o)
fonte
Ao usar o JetBrains IntelliJ, primeiro habilite "Anexar agente de memória" em Arquivo | Configurações | Compilação, Execução, Implantação | Depurador.
Ao depurar, clique com o botão direito do mouse em uma variável de interesse e escolha "Calcular tamanho retido":
fonte
Minha resposta é baseada no código fornecido por Nick. Esse código mede a quantidade total de bytes que são ocupados pelo objeto serializado. Portanto, isso realmente mede o material de serialização + a pegada de memória de objeto simples (apenas serialize, por exemplo,
int
e você verá que a quantidade total de bytes serializados não é4
). Portanto, se você deseja que o número de bytes brutos seja usado exatamente para o seu objeto - é necessário modificar um pouco esse código. Igual a:Eu testei esta solução com tipos primitivos, String e em algumas classes triviais. Pode não haver casos cobertos também.
UPDATE: Exemplo modificado para suportar o cálculo da pegada de memória de objetos de matriz.
fonte
Você pode gerar um dump de heap (com jmap, por exemplo) e depois analisar a saída para encontrar tamanhos de objeto. Esta é uma solução offline, mas você pode examinar tamanhos rasos e profundos etc.
fonte
size fornece o aumento no uso de memória da jvm devido à criação do objeto e esse normalmente é o tamanho do objeto.
fonte
Esta resposta não está relacionada ao tamanho do objeto, mas quando você estiver usando a matriz para acomodar os objetos; quanto tamanho de memória será alocado para o objeto.
Portanto, matrizes, lista ou mapeamento de todas essas coleções não armazenam objetos realmente (somente no momento das primitivas, é necessário o tamanho real da memória do objeto), ele armazenará apenas referências para esses objetos.
Agora o
Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
PRIMITIVOS
OBJETOS
Quero dizer que todo o objeto REFERENCE precisa de apenas 4 bytes de memória. Pode ser referência de string OU referência de objeto duplo, mas, dependendo da criação do objeto, a memória necessária variará.
por exemplo) Se eu criar um objeto para a classe abaixo
ReferenceMemoryTest
, 4 + 4 + 4 = 12 bytes de memória serão criados. A memória pode ser diferente quando você está tentando inicializar as referências.Portanto, ao criar uma matriz de objeto / referência, todo o seu conteúdo será ocupado com referências NULL. E sabemos que cada referência requer 4 bytes.
E, finalmente, a alocação de memória para o código abaixo é de 20 bytes.
ReferenceMemoryTest ref1 = new ReferenceMemoryTest (); (4 (ref1) + 12 = 16 bytes) ReferenceMemoryTest ref2 = ref1; (4 (ref2) + 16 = 20 bytes)
fonte
Suponha que eu declare uma classe chamada
Complex
como:Para ver quanta memória é alocada para instâncias ativas dessa classe:
fonte
Para JSONObject, o código abaixo pode ajudá-lo.
retorna tamanho em bytes
Eu verifiquei com meu objeto JSONArray gravando-o em um arquivo. Está dando tamanho do objeto.
fonte
Duvido que você queira fazê-lo programaticamente, a menos que queira fazê-lo apenas uma vez e armazená-lo para uso futuro. É uma coisa cara de se fazer. Não há operador sizeof () em Java e, mesmo que houvesse, contaria apenas o custo das referências a outros objetos e o tamanho das primitivas.
Uma maneira de fazer isso é serializar a coisa em um arquivo e observar o tamanho do arquivo, assim:
Obviamente, isso pressupõe que cada objeto é distinto e não contém referências não transitórias a qualquer outra coisa.
Outra estratégia seria pegar cada objeto e examinar seus membros refletindo e somar os tamanhos (booleano e byte = 1 byte, curto e char = 2 bytes, etc.), percorrendo a hierarquia de membros. Mas isso é tedioso e caro e acaba fazendo a mesma coisa que a estratégia de serialização faria.
fonte
java.lang.Integer
produz cerca de 80 bytes, onde a representação de heap geralmente é 32 (diferente da representação de fluxo de objetos, a representação de heap depende do tamanho do ponteiro e do alinhamento do objeto). Por outro lado, umanull
referência serializada requer um byte em vez dos quatro ou oito bytes na memória heap.