Java memory leak caused by MySQL libraries - java

I have a thread that executes and updates a database with some values. I commented out all the operations done to the data and just left the lines you can see below. While running the program with the lines you see bellow I get a memory leak.
This is what it looks like in VisualVM
Mysql Class
public ResultSet executeQuery(String Query) throws SQLException {
statement = this.connection.createStatement();
resultSet = statement.executeQuery(Query);
return resultSet;
}
public void executeUpdate(String Query) throws SQLException {
Statement tmpStatement = this.connection.createStatement();
tmpStatement.executeUpdate(Query);
tmpStatement.close();
tmpStatement=null;
}
Thread file
public void run() {
ResultSet results;
String query;
int id;
String IP;
int port;
String SearchTerm;
int sleepTime;
while (true) {
try {
query = "SELECT * FROM example WHERE a='0'";
results = this.database.executeQuery(query);
while (results.next()) {
id = results.getInt("id");
query = "UPDATE example SET a='1' WHERE id='"
+ id + "'";
SearchTerm=null;
this.database.executeUpdate(query);
}
results.close();
results = null;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The problem has happened with many other people after researching the web,
https://forum.hibernate.org/viewtopic.php?f=1&t=987128
Is bone cp or mysql.jdbc.JDBC4Connection known for leaking?
and a few more if you google "jdbc4resultset memory leak"

The leak is yours, not MySQL's. You aren't closing the statement.
The design of your method is poor. It should close the statement, and it should return something that will survive closing of the statement, such as a CachedRowSet.
Or else it should not exist at all. It's only three lines, and it doesn't support query parameters, so it isn't really much use. I would just delete it.
You also appear to have statement as an instance member, which is rarely if ever correct. It should be local to the method. At present your code isn't even thread-safe.
You should also be closing the ResultSet in a finally block to ensure it gets closed. Ditto the Statement.

Make sure that you are explicitly closing the database connections.

Related

Java try-with-resource on SQL statement will these close properly?

I had a very sophisticated class that performed DB queries, the problem is it wasnt using try-with-resource statements so i had to .close() manually. To be safer, I tried to re-design it with try-with-resource. My question is if these resources will close properly given how I'm referencing them outside the objects containing those resources. For example, this class DBQuery i use to create queries and resources related to those queries
public class DBQuery {
private String _query;
private PreparedStatement _stmt;
private ResultSet _rs;
// constructor
public DBQuery (String query) {
_query = query;
}
public PreparedStatement execPreparedStatement() throws SQLException {
_stmt = DB.getCon().prepareStatement(_query, Statement.RETURN_GENERATED_KEYS);
return _stmt;
}
public ResultSet getRecordSet() throws SQLException {
_rs = _stmt.executeQuery();
return _rs;
}
public void setInt(int paramNum, int setVal) throws SQLException {
_stmt.setInt(paramNum, setVal);
}
public void setString(int paramNum, String setVal) throws SQLException {
_stmt.setString(paramNum, setVal);
}
}
Then this would be example usage of the class. loadActiveCompany given a companyId retrieves the company from the database and creates some objects. My question is two-fold:
will the resources close properly when loadActiveCompany completes.
is there any problem with how I'm using the try-catch blocks.
Thank you
// loads the active company into the view
public void loadActiveCompany(int companyId) {
boolean loadFailed = false;
// we are passed the company id
_activeCompany.setCompanyId(companyId);
DBQuery qComps = new DBQuery("SELECT comp_name FROM comps WHERE id=?");
try ( PreparedStatement stmtComps = qComps.execPreparedStatement() ) {
DB.getCon().rollback();
qComps.setInt(1, companyId);
try ( ResultSet rsComps = qComps.getRecordSet() ) {
rsComps.next();
String companyName = rsComps.getString("comp_name");
_activeCompany.setCompanyName(companyName);
}
} catch (SQLException e) {
System.out.println("Couldn't find company!");
loadFailed = true;
} finally {
if (loadFailed)
return;
}
DBQuery qGroups = new DBQuery("SELECT id, group_name FROM comps_groups WHERE comp_id=? ORDER BY sort_order ASC");
try ( PreparedStatement stmtGroups = qGroups.execPreparedStatement() ) {
qGroups.setInt(1, companyId);
try ( ResultSet rsGroups = qGroups.getRecordSet() ) {
while (rsGroups.next()) {
int groupId = rsGroups.getInt("id");
Group thisGroup = new Group();
thisGroup.setGroupId(groupId);
thisGroup.setGroupName(rsGroups.getString("group_name"));
DBQuery qAnchors = new DBQuery("SELECT id, anchor_name, anchor_type FROM comps_groups_anchors WHERE group_id=? ORDER BY sort_order ASC");
try ( PreparedStatement stmtAnchors = qAnchors.execPreparedStatement() ) {
qAnchors.setInt(1, groupId);
try ( ResultSet rsAnchors = qAnchors.getRecordSet() ) {
while (rsAnchors.next()) {
int anchorId = rsAnchors.getInt("id");
Anchor thisAnchor = new Anchor();
thisAnchor.setAnchorId(anchorId);
thisAnchor.setAnchorName(qAnchors.getRS().getString("anchor_name"));
thisAnchor.setAnchorType(qAnchors.getRS().getInt("anchor_type"));
thisGroup.addGroupAnchor(thisAnchor);
}
}
}
_activeCompany.getCompanyGroups().add(thisGroup);
}
}
DB.getCon().commit();
} catch (SQLException e) {
System.out.println("Could not load company!");
loadFailed = true;
} finally {
if (loadFailed)
return;
}
// print out the active company
_activeCompany.printStatus();
}
Short answers:
Yes, the PreparedStatements are all closed.
Not directly problems, but easier ways.
Here my BUTs:
The name execPreparedStatement is totally misleading, as it is not (in DB terms) executing anything, just creating the PreparedStatement. A better name would be createPreparedStatement or - lol - preparePreparedStatement
Why do you call DB.getCon().rollback();? I do not think this will lead to someplace good...
the way you use DBQuery at the moment, it will only bring pain. Basicall this is just a container that saves additional infos (_stmt + _rs) which makes it SEVERELY state and sequence dependent, prone to hit you with lots of NPEs
so the actions you call on DBQuery you could simply also call on the PreparedStatement, reducing complexity and taking away a few pitfalls
so either completely remove DBQuery
or remodel the DBQuery
to be Closeable/AutoCloseable,
add some checks to the other functions,
create the PreparedStatement right away (CTOR, query string as CTOR parameter),
keep it private, do not expose it
use it inside getRecordSet,
do not store any other references unless you REALLY need them
and in the close method close the PS,
Your loadFailed = true; and if (loadFailed) return; seems overly convoluted and error-prone. Why not directly call return; right where you currently have loadFailed = true; lines?
I would - personal preference - put those 2 whole try-catch blocks into their own methods, signaling failure with a boolean or something => more methods with each less code and better variable scope (for example no re-use of loadFailed, but better re-usability of the two methods)
You actually do NOT need the inner try-resource on the ResultSets, but it's good if you (can) keep em. Just be careful there, as closing a ResultSet might have an impact on its creator PreparedStatement. So if you test it (in a situation where you re-use the PreparedStatement, that what it's actually made for) and get a 'closed' Exception when reusing the PreparedStatement, then you remove the try-resource blocks around the ResultSets.

JDBC ResultSet closed in Java after several iterations

I am having a problem with a ResultSet being closed. What confuses me is that it works for a portion of the data and then closes. At first I thought it might be because of connection timeout but that doesn't seem the case.
This portion of the program pertains to comparing an .xlsx workbook to an already present SQL database and for lack of a better term merges/updates it.
First, in my CompareDatabase class I am calling a search function that searches an SQLite database for a specific string every 6 iterations.
int columnCount = 6;
dataPoint = dataPoint.replaceAll("Detail", "");
String[] temp = dataPoint.trim().split("\\s+");
System.out.println(Arrays.toString(temp));
for (String tempDataPoint : temp) {
if ( columnCount == 6) {
System.out.println(search(tempDataPoint, connection));
}
columnCount = 0;
} else {
columnCount++;
}
}
This search function (also in the CompareDatabase class is then supposed to search for the value and return a String (was originally a Boolean but I wanted to see the output).
private String search (String searchValue, Connection connection) throws SQLException {
PreparedStatement pStatement = null;
pStatement = connection.prepareStatement("SELECT * FROM lotdatabase where (Vehicle) = (?)");
pStatement.setString(1, searchValue);
try (ResultSet resultSet = pStatement.executeQuery()){
return resultSet.getString(1);
}finally {
close(pStatement);
}
}
At the end you can see that the PreparedStatement is closed. The ResultSet should also be closed automatically (I read somewhere) but JDBC could possibly be being unreliable.
The Connection however is still open as it will be searching some 200+ strings and opening and closing that many times did not seem like a good idea.
These functions are called by my main class here:
One is commented out since it will error out because of primary key violation.
public static void main(String[] args) {
SQLDatabase sqlDatabase = new SQLDatabase();
//sqlDatabase.convertToSQL("Database1.xlsx");
sqlDatabase.compare("Database2.xlsx");
}
I have a suspicion that I am going about a bunch of this wrong (on the aspect of managing connections an such) and I would appreciate a reference to where I can learn to do it properly.
Also, being that PreparedStatement can only handle one ResultSet I don't see that being my issue since I close it every iteration in the for loop.
If more code or explanation is required please let me know and I will do my best to assist.
Thank you for taking the time to read this.
So after a bit more Googling and sleeping on it here is what worked for me.
The search function in compareDatabase changed to this:
private Boolean search (String searchValue, Connection connection) {
PreparedStatement ps = null;
try {
ps = connection.prepareStatement("SELECT * FROM lotdatabase where " +
"(Vehicle) = (?)");
ps.setString(1, searchValue);
ResultSet resultSet = ps.executeQuery();
//The following if statement checks if the ResultSet is empty.
if (!resultSet.next()){
resultSet.close();
ps.close();
return false;
}else{
resultSet.close();
ps.close();
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
And in the other function within compareDatabase I call the search function like this:
if (search(tempDataPoint, connection)) {
System.out.println("MATCH FOUND: " + tempDataPoint);
}else {
System.out.println("NOT FOUND: " + tempDataPoint);
}
This allows me to check the ResultSet and also be sure that it is closed.

MYSQL Insert query not updating data to the table in JAVA

I am working on a JAVA program which need to update database from text files. I have successfully inserted and updated data. But i am facing a problem with here, this method. Query runs without error and giving me the response. But the database table is not updating.
private void filteData() {
System.out.println("filteData");
Statement statementAtenLogInsert = null;
Statement statementqCheck = null;
Statement statementUpdateProLog = null;
Statement statementEnterError = null;
ResultSet rs = null;
int rcount;
//Update successfull attendance_test
String attenLogInsertSuccess = "INSERT INTO attendance_log (user_id, check_in, check_out) SELECT user_id, check_in, check_out FROM process_log WHERE flag = 'S'";
try {
statementAtenLogInsert = connection.createStatement();
statementAtenLogInsert.execute(attenLogInsertSuccess);
int qSuccess = statementAtenLogInsert.executeUpdate(attenLogInsertSuccess);
System.out.println("qSuccess " + qSuccess);
if(qSuccess > 0){
String deleteProcessLog = "DELETE FROM process_log WHERE flag = 'S'";
statementAtenLogInsert.execute(deleteProcessLog);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Here the attenLogInsertSuccess and deleteProcessLog queries are not working. Mean nothing happened from database table side. But qSuccess giving me a value. That means attenLogInsertSuccess is triggering. But nothing happened from mysql side.
You need to close your connection in order to flush the changes to the database.
Try adding connection.close(); somewhere in your pipeline, typically you close the connection in a finally block to ensure it is always closed but it appears you have defined your connection elsewhere, presumably for re-use in the calling function.
You also need to close your statements before closing the connection. See this similar answer for the pattern.

JDBC optimize MySql request on Multithread

I'm building a webcrawler and I'm looking for the best way to handle my requests and connection between my threads and the database (MySql).
I've 2 types of threads :
Fetchers : They crawl websites. They produce url and add they into 2 tables : table_url and table_file. They select from table_url
to continue the crawl. And update table_url to set visited=1 when they
have read a url. Or visited=-1 when they are reading it. They can
delete row.
Downloaders : They download files. They select from table_file. They update table_file to change the Downloaded column. They never
insert anything.
Right now I'm working with this :
I've a pool of connection based on c3p0.
Every target (website) have thoses variables :
private Connection connection_downloader;
private Connection connection_fetcher;
I create both connection only once when I instanciate a website. Then every thread will use thoses connections based on their target.
Every thread have thoses variables :
private Statement statement;
private ResultSet resultSet;
Before every Query I open a SqlStatement :
public static Statement openSqlStatement(Connection connection){
try {
return connection.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
And after every Query I close sql statement and resultSet with :
public static void closeSqlStatement(ResultSet resultSet, Statement statement){
if (resultSet != null) try { resultSet.close(); } catch (SQLException e) {e.printStackTrace();}
if (statement != null) try { statement.close(); } catch (SQLException e) {e.printStackTrace();}
}
Right now my Select queries only work with one select (I never have to select more than one for now but this will change soon) and is defined like this :
public static String sqlSelect(String Query, Connection connection, Statement statement, ResultSet resultSet){
String result = null;
try {
resultSet = statement.executeQuery(Query);
resultSet.next();
result = resultSet.toString();
} catch (SQLException e) {
e.printStackTrace();
}
closeSqlStatement(resultSet, statement);
return result;
}
And Insert, Delete and Update queries use this function :
public static int sqlExec(String Query, Connection connection, Statement statement){
int ResultSet = -1;
try {
ResultSet = statement.executeUpdate(Query);
} catch (SQLException e) {
e.printStackTrace();
}
closeSqlStatement(resultSet, statement);
return ResultSet;
}
My question is simple : can this be improved to be faster ? And I'm concerned about mutual exclusion to prevent a thread to update a link while another is doing it.
I believe your design is flawed. Having one connection assigned full-time for one website will severly limit your overall workload.
As you already have setup a connection pool, it's perfectly okay to fetch before you use (and return afterwards).
Just the same, try-with-catch for closing all your ResultSets and Statements after will make code more readable - and using PreparedStatement instead of Statement would not hurt as well.
One Example (using a static dataSource() call to access your pool):
public static String sqlSelect(String id) throws SQLException {
try(Connection con = dataSource().getConnection();
PreparedStatement ps = con.prepareStatement("SELECT row FROM table WHERE key = ?")) {
ps.setString(1, id);
try(ResultSet resultSet = ps.executeQuery()) {
if(rs.next()) {
return rs.getString(1);
} else {
throw new SQLException("Nothing found");
}
}
} catch (SQLException e) {
e.printStackTrace();
throw e;
}
}
Following the same pattern I suggest you create methods for all the different Insert/Update/Selects your application uses as well - all using the connection only for the short time inside the DB logic.
I can not see a real advantage to have all the Database stuff in your webcrawler threads.
Why don't you use a static class with the sqlSelect and sqlExec method, but without the Connection and ResultSet parameters. Both connection objects are static as well. Make sure the connection objects are valid befor using them.

Why connection throw timeoutException without commit

I have a DAO class, that has method below. I call this one inside Transaction manager. When I ran it without "conn.commit()" line - it throws timeout exception, but when I ran it with this one - it is ok. What's the problem? As I know it is not necessary to commit if you not modify db?
#Override
public List<String> getLinks(int id) throws SQLException {
List<String> list = new ArrayList<>();
Connection conn = factory.newConnection();
Statement statement = null;
ResultSet rs = null;
try {
String expression = "select link from users.links where id=" + id + " order by id_link desc";
statement = conn.createStatement();
rs = statement.executeQuery(expression);
while (rs.next()) {
list.add(rs.getString("link"));
}
// !!!!!!!!!!!!! without next line method throw TimeoutException
conn.commit(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return list;
} catch (SQLException e) {
rollBackQuietly(conn);
e.printStackTrace();
} finally {
closeQuaitly(rs);
closeQuaitly(statement);
closeQuaitly(conn);
}
return null;
}
Seeing as the commit() call is after the line which is throwing the exception, this problem must come after repeated invocations of this method (useful information to include in your question). this leads me to believe that your Connection factory is re-using Connections, and that it is handing out "stale" Connections (Connections which have been sitting around for too long and are no longer usable). if this is all true, then you need to make your factory manage Connections better. if it is a reasonably built connection pool, it probably has some feature like "test while idle" or "test on get" which you need to enable.

Categories

Resources