ASF Bugzilla – Attachment 35956 Details for
Bug 62432
Memory Leak in Statement Finalizer?
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
proposal for StatementFinalizer with regular cleanup of statements list
StatementFinalizer.java (text/x-csrc), 5.75 KB, created by
riegerm
on 2018-06-06 14:23:06 UTC
(
hide
)
Description:
proposal for StatementFinalizer with regular cleanup of statements list
Filename:
MIME Type:
Creator:
riegerm
Created:
2018-06-06 14:23:06 UTC
Size:
5.75 KB
patch
obsolete
>/* > * Licensed to the Apache Software Foundation (ASF) under one or more > * contributor license agreements. See the NOTICE file distributed with > * this work for additional information regarding copyright ownership. > * The ASF licenses this file to You under the Apache License, Version 2.0 > * (the "License"); you may not use this file except in compliance with > * the License. You may obtain a copy of the License at > * > * http://www.apache.org/licenses/LICENSE-2.0 > * > * Unless required by applicable law or agreed to in writing, software > * distributed under the License is distributed on an "AS IS" BASIS, > * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > * See the License for the specific language governing permissions and > * limitations under the License. > */ >package org.apache.tomcat.jdbc.pool.interceptor; > >import java.lang.ref.WeakReference; >import java.lang.reflect.Method; >import java.sql.Statement; >import java.util.LinkedList; >import java.util.List; >import java.util.Map; > >import org.apache.juli.logging.Log; >import org.apache.juli.logging.LogFactory; >import org.apache.tomcat.jdbc.pool.ConnectionPool; >import org.apache.tomcat.jdbc.pool.PoolProperties; >import org.apache.tomcat.jdbc.pool.PooledConnection; > >/** > * This is a variant implementation of {@link org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer}. > * to remedy a memory leak in the original version. > * > * The memory leak is found in the {@link StatementFinalizer.statements}-list, > * which holds a list of (weak references) to SQL statements. Once the statement is > * closed by the client code (and therefore the StatementFinalizer is no longer interested > * in the statement, it gets garbage collected, since the statement finalizer only holds > * a weak reference. What is *not* garbarge collected are the instances of > * {@link StatementFinalizer$StatementEntry} holding the empty reference. They are only removed > * when the connection is closed. For connections that stay open long, the list keeps > * growing eventually leading to an out of memory. > * > * This code is basically a copy of the original code, as the change we have done > * was done very invasively and we had to access prviate members. > * Our new method is {@link StatementFinalizer.cleanupStatementList()} > */ >public class StatementFinalizer extends AbstractCreateStatementInterceptor >{ > private static final Log log = LogFactory.getLog(StatementFinalizer.class); > > protected List<StatementEntry> statements = new LinkedList<>(); > > private boolean logCreationStack = false; > > @Override > public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) > { > cleanupStatementList(); > try { > if (statement instanceof Statement) > statements.add(new StatementEntry((Statement)statement)); > } > catch (ClassCastException x) { /*ignore this one*/ } > return statement; > } > > /** > * Removes from the list all {@link statements} "dead" {@link StatementEntry} > * instances, i.e. instances whose (weak) reference to the Statement is empty, > */ > private void cleanupStatementList() > { > // in the statements list, older entries are at the front > // as new ones are appended at the end. > // It therefore makes sense for us to start at the front > // in our search for expired entries > // We're going linearly through this list, which I hope is not > // a performance problem. By cleaning up the list every time > // we add a new element, we keep the list as small as possible > int pos = 0; > while(pos < statements.size()) > { > Statement st = statements.get(pos).getStatement(); > if(st == null) > { > statements.remove(pos); > } > else > { > ++pos; > } > } > } > > @SuppressWarnings("null") // st is not null when used > @Override > public void closeInvoked() > { > while (statements.size()>0) > { > StatementEntry ws = statements.remove(0); > Statement st = ws.getStatement(); > boolean shallClose = false; > try { > shallClose = st!=null && (!st.isClosed()); > if (shallClose) { st.close(); } > } > catch (Exception ignore) > { > if (log.isDebugEnabled()) { > log.debug("Unable to closed statement upon connection close.",ignore); > } > } > finally > { > if (logCreationStack && shallClose) { > log.warn("Statement created, but was not closed at:", ws.getAllocationStack()); > } > } > } > } > @Override > public void setProperties(Map<String, PoolProperties.InterceptorProperty> properties) > { > super.setProperties(properties); > PoolProperties.InterceptorProperty logProperty = properties.get("trace"); > if (null != logProperty) { > logCreationStack = logProperty.getValueAsBoolean(logCreationStack); > } > } > @Override > public void reset(ConnectionPool parent, PooledConnection con) > { > statements.clear(); > super.reset(parent, con); > } > protected class StatementEntry > { > private WeakReference<Statement> statement; > private Throwable allocationStack; > > public StatementEntry(Statement statement) > { > this.statement = new WeakReference<>(statement); > if (logCreationStack) { > this.allocationStack = new Throwable(); > } > } > public Statement getStatement() { > return statement.get(); > } > public Throwable getAllocationStack() { > return allocationStack; > } > } >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 62432
: 35956