MySQL não presta?

17 respostas
M

Pessoal, preciso muito de ajuda e não sei mais q quem recorrer, eis o problema

minha aplicação extrai informações de texto

ela extraia os dados e armazenava em buffers na memoria. O que acontece e q com isso aumentava o uso de RAM e esta memoria não estava sendo liberada pelo GC

decidi entao armazenar os dados no SGBD MySQL, so q mesmo assim o consumo de memoria nao reduziu

Eu ja usei todas as dicas q sabia para evitar memory leak, atribui null para objetos, reutilizei os objetos, etc etc etc, mas mesmo assim nao consigo reduzir o uso de memoria

pergunto a vcs:

O uso de expressões regulares consome muita RAM ?
Alguem poderia me dizer se o MySQL e o mais indicado pra isso ?
Alguem poderia me dar um exemplo efetivo de insert em banco sem memory leak ?

17 Respostas

Roger75

Bem, pode ser várias coisas, mas pelo que você diz parece ser caso de conexão ou statement que foi aberto mas não foi fechado (deve chamar o método close() no objeto).

MarkKnopfler

Pra responder suas questões, só vendo com mais detalhes a forma como vc está gravando esses dados de texto.
De alguma maneira eles estão se acumulando na memória mesmo inserindo no banco de dados. Há leaks de memória que são bem sutis.

M

segue a forma como estou inserindo os dados

public static void insereLinkBanco(String tabela, String campo, String info) {
		
		try {
			
			String sql = "INSERT INTO " + tabela + " (" + campo + ") VALUES (\'" + info + "\')";
				 	
			Statement stm = conn.createStatement();
			                 
			stm.execute(sql);
			stm.close();
	
			stm = null;
			sql = null;
				
		} catch (SQLException e) {
			
			e.printStackTrace();
				
		}// catch 
		
	}// end of insereLinkBanco

e a forma como faço a conexão com o banco

// configurações do banco
	private static final String DRIVER = "com.mysql.jdbc.Driver";
	private static final String DATABASE_URL = "jdbc:mysql://localhost:3306/Banco";
	private static final String LOGIN = "root";
	private static final String SENHA = "";
	
	private static  Connection conn = null;

	/***/
	public static void conectaBancoDados() {

		try {
            
			// carrega o driver
			Class.forName(DRIVER).newInstance();
			
			// conecta
			conn = DriverManager.getConnection(DATABASE_URL, LOGIN, SENHA);

		} catch (Exception e) {
			
			e.printStackTrace();
			
		}// catch
		
	}// end of conectaBancoDados

eu faço a conexão com o banco apenas 1 vez, nem usando StringBuilder pra montar a SQL melhora a performance

ViniGodoy

Chegou a rodar um profiler?

M

Viny, perdoe a burrice, rodei o JProfiler mas não soubre interpretar os resultados

rmendes08

Bom, respondendo à primeira pergunta: Sim. Processar expressões regulares pode exigir bastante memória e processamento. Mas para ter certeza do que está consumindo a memória da sua aplicação só com o profiler mesmo. Eu gosto muito da VisualVM, que vem junto com a JDK. Tem uma aba nela que mostra qual a classe cujos objetos estão consumindo mais memória, com o VisualGC dá pra saber qual região da heap está consumindo mais espaço. São pistas bastante valiosas para enconrtar gargalos.

Tente compartilhar o resultado do profile de alguma maneira. O código do processamento das regex pode ser muito útil também.

Hebert_Coelho

Roger75:
Bem, pode ser várias coisas, mas pelo que você diz parece ser caso de conexão ou statement que foi aberto mas não foi fechado (deve chamar o método close() no objeto).
+1
No meu trabalho usa massivamente o MySQL e não tem MemoryLeak. Sorry but MySQL presta, diversos códigos que o acessam que não.

rmendes08:
Bom, respondendo à primeira pergunta: Sim. Processar expressões regulares pode exigir bastante memória e processamento. Mas para ter certeza do que está consumindo a memória da sua aplicação só com o profiler mesmo. Eu gosto muito da VisualVM, que vem junto com a JDK. Tem uma aba nela que mostra qual a classe cujos objetos estão consumindo mais memória, com o VisualGC dá pra saber qual região da heap está consumindo mais espaço. São pistas bastante valiosas para enconrtar gargalos.

Tente compartilhar o resultado do profile de alguma maneira. O código do processamento das regex pode ser muito útil também.

+1
Faça como você fez, peça ajuda. Se você não sabe analisar, dá um pulo aqui com o resultado da análise que alguém dá dica. Ou até mesmo procure no google: “how to analyze jprofiler result”. [=

M

bom saber :slight_smile:

Eu vou prepara alguns testes aqui e posto os resultados

M

troquei o MySQL pelo H2, no meu caso bastou alterar o driver e o endereço do BD pra ver se melhorava a performance, mas ate agora o resultado dos testes não esta satisfatório

Os dados do profiler indicam ação do GC, liberação de objetos porém o uso de memoria não é reduzido, pelo contrario, esta crescente
O uso da Heap apresenta-se pela metade da capacidade. Reparei que usando a aplicação sem o BD, depois de certo tempo ocorre liberação de RAM, com o BD isto não esta ocorrendo.

Como a aplicação deve ser usada por um longo tempo, o uso de BD torna-se necessario, vou ver se coloco alguns prints aqui, alguma sugestão do que possa estar ocorrendo ?

M

segue o codigo de manipulação do meu BD

import static java.lang.System.out;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public final class Bd {

	// configurações do banco
	private static final String DRIVER = "org.h2.Driver";
	private static final String DATABASE_URL = "jdbc:h2:Spiderdb";
	private static final String LOGIN = "sa";
	private static final String SENHA = "";
	
	private static Connection conn = null;
	
	/***/
	public static void conectaBancoDados() {
	
		try {
		
			out.println("Conectando com banco de dados");
			
			// carrega o driver
			Class.forName(DRIVER);
			
			// conecta
			conn = DriverManager.getConnection(DATABASE_URL, LOGIN, SENHA);
			
			out.println("Conectado !");
			
		} catch (Exception e) {
			
			e.printStackTrace();
			
		}// catch
			
	}// end of conectaBancoDados
	
	/***/
	public static void criaTabelas() {
		
		try {
				
			Statement stm = conn.createStatement(); 
			
			String sql1 = "CREATE TABLE IF NOT EXISTS Visitar(ver VARCHAR(767) NOT NULL, UNIQUE(ver))";
			String sql2 = "CREATE TABLE IF NOT EXISTS Visitado(visto VARCHAR(767) NOT NULL, UNIQUE(visto))";
			
			
			out.println("Criando tabela Visitar...");
				stm.execute(sql1);
							
			out.println("Criando tabela Visitado...");
				stm.execute(sql2);
				
			stm.close();
			
			stm = null; 
			sql1 = null;
			sql2 = null;
					
		} catch (SQLException e) {
			
			e.printStackTrace();
				System.exit(0); 
		
		}// catch
		
	}// end of criaTabelas
	
	/***/
	public static void insereLinkBanco(String tabela, String campo, String info) {
		
		try {
			
			String sql = "INSERT INTO " + tabela + " (" + campo + ") VALUES (\'" + info + "\')";
			
			Statement stm = conn.createStatement();
			                 
			stm.execute(sql);
			stm.close();
			
			stm = null;
			sql = null;
				
		} catch (SQLException e) {
			
			e.printStackTrace();
				
		}// catch 

	}// end of insereLinkBanco
	
	/***/
	public static String selecionaLinkBanco(String tabela, String campo) { 
		
		String resultado = null;

		try {			
			
			String sql = "SELECT " + campo + " FROM " + tabela + " WHERE 1 LIMIT 0, 1";
			 
			Statement stm = conn.createStatement();
			                  
			ResultSet rs = stm.executeQuery(sql);
			
			// obtem o resultado do comando
			while (rs.next())
				resultado = rs.getString(campo);
					
			stm.close();
			rs.close();
		
			stm = null;
			sql = null;
		    rs = null;	
		
		} catch (SQLException e) {
			
			e.printStackTrace();
			
		}// catch 
		
		return resultado;
	
	}// end of selecionaLinkBanco
	
	/***/
	public static void removeLinkBanco(String tabela, String campo, String valor) {
		
		try {
		
			String sql = "DELETE FROM " + tabela + " WHERE " + campo + " = \'" +  valor + "\'";
			 
			Statement stm = conn.createStatement();
			                  
			stm.execute(sql);
			stm.close();
		
			stm = null;
			sql = null;
				
		} catch (SQLException e) {
			
			e.printStackTrace();
			
		}// catch
		
	}// end of removeLinkBanco 
	
	/***/
	public static int totalLinks(String tabela, String campo) {
		
		int resultado = 0;
		
		try {
			
			String sql = "SELECT COUNT(" + campo + ") FROM " + tabela;
			
			Statement stm = conn.createStatement();
			                 
			ResultSet rs = stm.executeQuery(sql);
			
			while (rs.next())
				resultado = rs.getInt(1);
			
			stm.close();
			rs.close();
			
			stm = null;
			rs = null;
			sql = null;
			
		} catch (SQLException e) {

			return -1;
			
		}// catch
		
		return resultado;
		
	}// end of totalLinks
	
	/***/
	public static boolean existeNaTabela(String tabela, String campo, String valor) {
		
		boolean resultado = false;
		
		try {
					
			String sql = "SELECT " + campo + " FROM " + tabela + " WHERE " + campo + " = \'" + valor + "\'";
				
			Statement stm = conn.createStatement();
			                 
			ResultSet rs = stm.executeQuery(sql);
			
			if (rs.next())
				resultado = true;
			
			stm.close();
			rs.close();
			
			stm = null;
			rs = null;
			sql = null;
					
		} catch (SQLException e) {
			
			e.printStackTrace();
			
		}// catch 
		
		return resultado;
		
	}// end of existeNaTabela

}// end of class
rmendes08

Cara, coloca o código que chama esses métodos, vai ajudar bastante. Mas uma coisa é fato, você não está fechando seus Statements de maneira apropriada. O fechamento de Statements devem acontecer em um bloco finally, após o catch. Isso porque pode acontecer de você abrir um Statement, acontecer alguma exceção e o Statement ficar aberto. Outra coisa, ficar anulando variáveis locais no final dos métodos não ajuda em absolutamente nada o GC, só dificulta a leitura.

M

ok, vou adaptar o codigo

pensei que estaria acelerando o processo de GC, vc sugere alguma coisa para deletar os objetos ? algum momento oportuno ?

ps - a estrategia de manter so uma conexao com o BD esta correta ?

rmendes08

Não é necessário. Os algoritmos do GC são bastante sofisticados, e são bem eficientes na hora de coletar memória. Objetos criados em escopos de métodos e deixam de ser referenciados já no mesmo método, são coletados imediatamente após o término do método. O problema dos memory leaks são objetos mantidos entre chamadas de vários métodos. No código que você postou, os únicos candidatos são os Statements e ResultSet’s, porque na prática, eles são criados por um objeto Connection, e que são mantidos em memória até que sejam fechados.

Depende. Se a sua aplicação executar em uma única Thread não há problema algum, nesse caso, é até recomendável manter uma única conexão.

M

estou trabalhando com 5 threads, cada uma retirando e colocando dados no SGBD, por isso perguntei sobre a conexão

rmendes08

Nesse caso é interessante que cada Thread trabalhe com sua própria conexão com o banco. De qualquer maneira, se todas as threads atualizam a mesma tabela ao mesmo tempo, o seu ganho de desempenho será mínimo.

M

creio então que não seja necessário criar uma conexão para cada thread, preciso saber é como reduzir este consumo de RAM, o projeto esta em teste agora e estou avaliando o consumo

M

As informações com que trabalho são obtidas de paginas HTML. Para fazer o parser estou utilizando a biblioteca JSoup, porém observei que a mesma esta gastando uma memoria consideravel para realizar a tarefa, creio que usando regex o desempenho possa ser melhorado, me refiro a consumo de memoria.

Vcs concordam ? teriam alguma expressão regular dificil de quebrar para obter links e informações de uma pagina web ?

Criado 29 de novembro de 2012
Ultima resposta 3 de dez. de 2012
Respostas 17
Participantes 6