Problema de estouro de memória com docBuilder.parse

15 respostas
F
Galera do java, beleza?

Então, estou com um problema com estouro de memoria.java.lang.Exception: Erro na unificação do arquivo: arqnfSP112011

at br.gov.suframa.internamento.fisco.gui.FiscoFileMergerMainFrame$UnificacaoExecutavel.run(FiscoFileMergerMainFrame.java:548)

at java.lang.Thread.run(Unknown Source)

Caused by: java.lang.OutOfMemoryError: Java heap space

at java.util.Arrays.copyOfRange(Unknown Source)

at java.lang.String.<init>(Unknown Source)

at org.apache.xerces.xni.XMLString.toString(Unknown Source)

at org.apache.xerces.parsers.AbstractDOMParser.characters(Unknown Source)

at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanContent(Unknown Source)

at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)

at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)

at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)

at org.apache.xerces.parsers.XML11

Penso ser devido ao metodo docBuilder.parse que consome muita memoria. Alguém conhece algum substituto que faça o trabalho “sujo” sem consumir muita memoria, ou que trabalhe com a memoria do hard disk em vez da RAM.

Thanks.

15 Respostas

ViniGodoy

Por favor, ao postar tópicos não abuse das letras maiúsculas no título. Não tem porque chamar mais atenção que os demais.

Quanto ao seu problema, já experimentou aumentar o tamanho do heap da VM?
Por padrão, o da VM é bem pequeno (algo entre 32 ou 64 mb).

F

Não foi intenção, chamar a atenção de ninguem, mas deu certo. hehehe.

Então meu caro. Esta questão de aumentar o limite da memo da VM ja faiz e deu certo. Porém, o meu objetivo é realmente diminuir este uso de memória. Conhece alguma forma?

ViniGodoy

Procure algum parser de XML baseado em StAX:
http://woodstox.codehaus.org/
https://sjsxp.dev.java.net/

Ou escreva diretamente em arquivos. Como isso parece ser um “merger”, não deve ser muito difícil.

F

Caro.

Tentei usar o SAX e não consegui. Mas vou mais a fundo para resolver o problema com SAX. Quero somente saber ser essa biblioteca tem todas as funcionalidades que a Document.Builder faz.

ViniGodoy

Eu falei StAX e não SAX.

F

Opa! Tranquilo?
Estou usando StAX, como indicado. Dei uma lida na doc da sun sobre esta API.

Então, desenvolvi o codigo abaixo. Porém esta me lançando uma Exception:

função(InputStream input1,

InputStream input2){



XMLEventWriter eventWriter;

XMLEventFactory eventFactory = XMLEventFactory.newInstance();

XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
eventWriter = outputFactory
				.createXMLEventWriter(new FileOutputStream("testMerge1.xml"));

		XMLEvent newLine = eventFactory.createDTD("\notasfiscais");

		StartDocument startDocument = eventFactory.createStartDocument();

		eventWriter.add(startDocument);
		eventWriter.add(newLine);

		StartElement configStartElement = eventFactory.createStartElement(
				"", "", "TestCaseBlock");

		eventWriter.add(configStartElement);
		eventWriter.add(newLine);

		InputStream[] filenames = new InputStream[] { input1, input2 };

		for (InputStream filename : filenames) {

			XMLInputFactory factory = XMLInputFactory.newInstance();
			XMLEventReader testEventReader = factory.createXMLEventReader(input1);&lt;&lt;&lt;----[i][b]Aqui está o problema, n entendo o pq, ja que esta variavel input1 ja é uma inputStream criado apartir do XML e me enviado pela função.[/b][/i]

			while (testEventReader.hasNext()) {
				XMLEvent event = testEventReader.nextEvent();
				if (event.getEventType() != XMLEvent.START_DOCUMENT
						&& event.getEventType() != XMLEvent.END_DOCUMENT)
					eventWriter.add(event);
				eventWriter.add(newLine);
				testEventReader.close();
			}

		}

}

E a exception é : javax.xml.stream.XMLStreamException: java.io.IOException: Stream closed

at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.setInputSource(Unknown Source)

at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.<init>(Unknown Source)

at com.sun.xml.internal.stream.XMLInputFactoryImpl.getXMLStreamReaderImpl(Unknown Source)

at com.sun.xml.internal.stream.XMLInputFactoryImpl.createXMLStreamReader(Unknown Source)

at com.sun.xml.internal.stream.XMLInputFactoryImpl.createXMLEventReader(Unknown Source)

at br.gov.suframa.internamento.fisco.merger.impl.XMLMerger.merge(XMLMerger.java:121)

at br.gov.suframa.internamento.fisco.merger.impl.XMLMerger.mergeXML(XMLMerger.java:61)

at br.gov.suframa.internamento.fisco.merger.impl.XMLMerger.merge(XMLMerger.java:44)

at br.gov.suframa.internamento.fisco.FiscoFileMerger.executarUnificacao(FiscoFileMerger.java:94)

at br.gov.suframa.internamento.fisco.gui.FiscoFileMergerMainFrame$UnificacaoExecutavel.run(FiscoFileMergerMainFrame.java:552)

at java.lang.Thread.run(Unknown Source)

Caused by: java.io.IOException: Stream closed

at java.io.BufferedInputStream.getBufIfOpen(Unknown Source)

at java.io.BufferedInputStream.fill(Unknown Source)

at java.io.BufferedInputStream.read(Unknown Source)

at com.sun.org.apache.xerces.internal.impl.XMLEntityManager$RewindableInputStream.read(Unknown Source)

at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)

at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(Unknown Source)

at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startDocumentEntity(Unknown Source)

at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.setInputSource(Unknown Source)

thanks man.

E
while (testEventReader.hasNext()) {
    XMLEvent event = testEventReader.nextEvent();
    if (event.getEventType() != XMLEvent.START_DOCUMENT
    && event.getEventType() != XMLEvent.END_DOCUMENT)
        eventWriter.add(event);
    eventWriter.add(newLine);
    testEventReader.close();
}

Você fechou o testEventReader dentro do loop por quê?

F

Estou seguindo o padrão da documentação. Mas a implementação nem chega nesta lparte do codigo. Quebra justamente na linha que deixei destacada(linha 121). A exception é em cima dela.

F

Encontrei meu erro primário. Está no for() . Ja que ele dá 2 voltas, devido ao array filenames[]. Valeu Pessoal.

E
InputStream[] filenames = new InputStream[] { input1, input2 };

for (InputStream filename : filenames) {

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLEventReader testEventReader = factory.createXMLEventReader(input1);

Acho que você está fazendo um pouco de salada. O correto,no seu caso, provavelmente seria:

InputStream[] inputstreams = new InputStream[] { input1, input2 };

for (InputStream inputstream : inputstreams) {

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLEventReader testEventReader = factory.createXMLEventReader(inputstream);

E de modo geral, que documentação você está seguindo? (poste a URL)
Duvido que na documentação alguém fechasse um EventReader dentro do loop.

F

De modo geral, o que vc fez foi mudar o nome das variáveis, para nomes mais padronizados.

Nesta questão do close. Eu intendo que o EventReader seria cada linha o tag do xml, e que ele lê este Event, joga para o novo XML criado e fecha a linha, para que na proxima interação, ele pegue a proxima linha. Não sei se estou certo.

Tem como enviar uma sugestão neste caso do loop?

Estou seguindo um cod do stakeoverflow e não como erroneamente eu disse anteriormente.
http://stackoverflow.com/questions/5681597/how-to-merge-two-xmls-in-java

Thanks man.

E

Ah, o carinha escreveu um código que poderia ser resolvido com um “if”, não com um “while”.

Agora entendi mais ou menos a história do close().

É que normalmente se você faz um “close()” e depois chama “hasNext()”, em vez de o hasNext retornar false, ele dá uma exceção com uma mensagem mais ou menos do tipo “Stream already closed” ou coisa parecida.

De qualquer maneira, usar nomes mais padronizados evita você fazer bagunças como ter se esquecido de trocar o “input1”, naquela linha que deu o problema.

Se você trocou o tipo de String para InputStream, e fez algumas alterações a mais, é aconselhável você também mudar o nome para ficar mais fácil de você acompanhar o que deve ser feito.

F

E ai pessoal, aqui novamente. O que ma falta agora para finalizar é transformar este meu xml criado que está contido na variavel eventWriter em um inputStream ou ByteArrayInputStream para dar o return da função. Como fazer este cast? Como fazer isso? :?:

F

Migrei para o StAX, para fazer o merge, esta dando quase tudo certo. Mas estou tendo problemas com a organização das tags de dentro do xml. Outro assunto é a exception que está sendo lançada. Alguem para dar uma força? A unica organização que quero é a da tag principal !

file

... 70mb de arquivos

file1

... 5mb de arquivos

resultado esperado = teste

75mb de arquivos

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.XMLInputFactory;

public class main {

	public static void main(String[] args) throws FileNotFoundException,
			XMLStreamException {

		// TODO Auto-generated constructor stub


		XMLEventWriter eventWriter = null;
		XMLEventFactory eventFactory = null;
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();

		File file = new File("C:/XMLftp.xml");<---- Este arquivo ja existe
		File file1 = new File("C:/XMLLocal.xml");<---- Este arquivo ja existe
		File teste = new File("C:/XMLTESTE.xml");

		if (file.exists() && file1.exists()) {
			System.out.println("Os arquivos existem");
		} else {
			System.out.println("Não existem");

		}

		try {
			teste.createNewFile();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		eventWriter = outputFactory.createXMLEventWriter(new FileOutputStream(
				teste));
		eventFactory = XMLEventFactory.newInstance();
		
	

		XMLEvent newLine = eventFactory.createDTD("\n");

		StartDocument startDocument = eventFactory.createStartDocument();

		eventWriter.add(startDocument);
		eventWriter.add(newLine);

		StartElement configStartElement = eventFactory.createStartElement("",
				"", "notasfiscais");

		eventWriter.add(configStartElement);
		eventWriter.add(newLine);

		String[] filenames = new String[] {  "C:/XMLftp.xml" , "C:/XMLLocal.xml"  };

		for (String filename : filenames) {

			FileInputStream inputStream = new FileInputStream(filename);
	//InputStream inputReader = new FileInputStream(filename);
			
			XMLInputFactory inputFactory = XMLInputFactory.newInstance();
			
			XMLEventReader test = inputFactory
					.createXMLEventReader(inputStream);
			
			// docBuilderFactory.setNamespaceAware(true);
			// docBuilderFactory.setIgnoringElementContentWhitespace(true);

			int i = 0;
			while (test.hasNext()) {
				XMLEvent event = test.nextEvent();
				
				i++;
				// avoiding start(<?xml version="1.0"?>) and end of the
				// documents;
                
				if (event.getEventType() != XMLEvent.START_DOCUMENT
						&& event.getEventType() != XMLEvent.END_DOCUMENT )
						eventWriter.add(event);
					

				eventWriter.add(newLine);
				test.close();
			}

		}
		eventWriter.add(eventFactory.createEndElement("", "", "notasfiscais"));
	//	eventWriter.add(newLine);
		eventWriter.add(eventFactory.createEndDocument());
		eventWriter.close();
	}
}
Exception
Exception in thread "main" javax.xml.stream.XMLStreamException: No element was found to write: java.lang.ArrayIndexOutOfBoundsException: -1
	at com.sun.xml.internal.stream.writers.XMLStreamWriterImpl.writeEndElement(Unknown Source)
	at com.sun.xml.internal.stream.writers.XMLEventWriterImpl.add(Unknown Source)
	at main.main(main.java:94)
Caused by: java.lang.ArrayIndexOutOfBoundsException: -1
	at com.sun.xml.internal.stream.writers.XMLStreamWriterImpl$ElementStack.pop(Unknown Source)
	... 3 more
F

Fala galera. Depois de muito trabalho. Final cheguei onde gostaria. Merge usando StAX. Segue abaixo somente a função.
A lógica não é muito direta. Mas foi a forma que encontrei para resolver o problema. :!:

fileFtp:

<?xml version="1.0" encoding="UTF-8"?>
<notasfiscais>
<notafiscal>fdfdfdfd</notafiscal>
<notafiscal>fdsfgsd</notafiscal>
</notasfiscais>

fileLocal:

[code]<notasfiscais>
<notafiscal>rerer</notafiscal>
<notafiscal>saffdsf</notafiscal>
</notasfiscais>
[/code]

Obs: É claro que os arquivos principais existem para mais de 300.000 eventos, estes acima são somente para ilustrar.

teste <----- Resultado

<notasfiscais>
<notafiscal>fdfdfdfd</notafiscal>
<notafiscal>fdsfgsd</notafiscal>
<notafiscal>rerer</notafiscal>
<notafiscal>saffdsf</notafiscal>
</notasfiscais>
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;


import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.XMLInputFactory;

public class Teste {

//fileFtp e fileLocal são arquivos XML. Pelo menos o mapeamento dele. E levo em consideração que os dois existem!

	public InputStream merge(File fileFtp , File fileLocal) throws FileNotFoundException, XMLStreamException{

		// TODO Auto-generated constructor stub

		XMLEventWriter eventWriter = null;
		XMLEventFactory eventFactory = null;
		XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();

		File teste = new File("C:\\XMLTESTE.xml");

		try {
			teste.createNewFile();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		eventWriter = outputFactory.createXMLEventWriter(new FileOutputStream(
				teste));

		eventFactory = XMLEventFactory.newInstance();

		XMLEvent tagEspacoLinha = eventFactory.createDTD("\n");

		eventWriter.add(eventFactory.createStartDocument());
		eventWriter.add(tagEspacoLinha);
		eventWriter
				.add(eventFactory.createStartElement("", "", "notasfiscais"));

		String[] filenames = new String[] { "C:/XMLftp.xml", "C:/XMLLocal.xml" };

		boolean i = false;

		for (String filename : filenames) {

			i = true;

			FileInputStream inputStream = new FileInputStream(filename);

			XMLInputFactory inputFactory = XMLInputFactory.newInstance();

			XMLEventReader test = inputFactory
					.createXMLEventReader(inputStream);

			while (test.hasNext()) {
				XMLEvent event = test.nextEvent();
				XMLEvent event1 = test.peek();

				
				if(event.isStartElement()&&i==true){
					i = false;
					test.close();
					continue;					
				}
				
				if (event.getEventType() != XMLEvent.END_DOCUMENT
						&& event.getEventType() != XMLEvent.START_DOCUMENT
						&& !event1.isEndDocument()) {
						eventWriter.add(event);
									
				}
								
				test.close();
			}
			eventWriter.add(tagEspacoLinha);

		}
		eventWriter.add(eventFactory.createEndElement("", "", "notasfiscais"));
		eventWriter.close();
		
//Função extra.
		Teste testeNew = new Teste();
		return testeNew.transformar(teste);
	
	}
	
	public InputStream transformar(File file) throws FileNotFoundException{
		
		FileInputStream fis = new FileInputStream (file); 
		
		System.out.println("Entrou na função");
		return fis;
		
	}

}

Agora é só correr pro abraço!! Agradecimentos aos que deram a ideal de fazer por arquivo.

Criado 3 de abril de 2012
Ultima resposta 13 de abr. de 2012
Respostas 15
Participantes 3