diff --git a/router/java/src/net/i2p/router/transport/crypto/DHSessionKeyBuilder.java b/router/java/src/net/i2p/router/transport/crypto/DHSessionKeyBuilder.java
index 0d49a656aa01af69a95c9b27804e836461008e3a..e1368d83c93c58b02c99de92bcd3514e179f4830 100644
--- a/router/java/src/net/i2p/router/transport/crypto/DHSessionKeyBuilder.java
+++ b/router/java/src/net/i2p/router/transport/crypto/DHSessionKeyBuilder.java
@@ -436,6 +436,15 @@ public class DHSessionKeyBuilder {
          * or pulls a prebuilt one from the queue.
          */
         public DHSessionKeyBuilder getBuilder();
+
+        /**
+         * Return an unused DH key builder
+         * to be put back onto the queue for reuse.
+         *
+         * @param builder must not have a peerPublicValue set
+         * @since 0.9.16
+         */
+        public void returnUnused(DHSessionKeyBuilder builder);
     }
 
     public static class PrecalcRunner extends I2PThread implements Factory {
@@ -457,6 +466,7 @@ public class DHSessionKeyBuilder {
             ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*60*1000 });
             //ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*60*1000 });        
             ctx.statManager().createRateStat("crypto.DHUsed", "Need a DH from the queue", "Encryption", new long[] { 60*60*1000 });
+            ctx.statManager().createRateStat("crypto.DHReused", "Unused DH requeued", "Encryption", new long[] { 60*60*1000 });
             ctx.statManager().createRateStat("crypto.DHEmpty", "DH queue empty", "Encryption", new long[] { 60*60*1000 });
 
             // add to the defaults for every 128MB of RAM, up to 512MB
@@ -536,11 +546,11 @@ public class DHSessionKeyBuilder {
          * @since 0.9 moved from DHSKB
          */
         public DHSessionKeyBuilder getBuilder() {
-            _context.statManager().addRateData("crypto.DHUsed", 1, 0);
+            _context.statManager().addRateData("crypto.DHUsed", 1);
             DHSessionKeyBuilder builder = _builders.poll();
             if (builder == null) {
                 if (_log.shouldLog(Log.INFO)) _log.info("No more builders, creating one now");
-                _context.statManager().addRateData("crypto.DHEmpty", 1, 0);
+                _context.statManager().addRateData("crypto.DHEmpty", 1);
                 builder = precalc();
             }
             return builder;
@@ -551,7 +561,7 @@ public class DHSessionKeyBuilder {
             DHSessionKeyBuilder builder = new DHSessionKeyBuilder(_context);
             long end = System.currentTimeMillis();
             long diff = end - start;
-            _context.statManager().addRateData("crypto.dhGeneratePublicTime", diff, diff);
+            _context.statManager().addRateData("crypto.dhGeneratePublicTime", diff);
             if (diff > 1000) {
                 if (_log.shouldLog(Log.WARN))
                     _log.warn("Took more than a second (" + diff + "ms) to generate local DH value");
@@ -561,6 +571,23 @@ public class DHSessionKeyBuilder {
             return builder;
         }
 
+        /**
+         * Return an unused DH key builder
+         * to be put back onto the queue for reuse.
+         *
+         * @param builder must not have a peerPublicValue set
+         * @since 0.9.16
+         */
+        public void returnUnused(DHSessionKeyBuilder builder) {
+            if (builder.getPeerPublicValue() != null) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("builder returned used");
+                return;
+            }
+            _context.statManager().addRateData("crypto.DHReused", 1);
+            _builders.offer(builder);
+        }
+
         /** @return true if successful, false if full */
         private final boolean addBuilder(DHSessionKeyBuilder builder) {
             return _builders.offer(builder);
diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
index c7e4703a4d409c286f2799c9f9b08e691f245fcb..f1269c772f09707b5732c1f2c914653ab6484163 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java
@@ -775,6 +775,17 @@ public class NTCPTransport extends TransportImpl {
         return _dhFactory.getBuilder();
     }
 
+    /**
+     * Return an unused DH key builder
+     * to be put back onto the queue for reuse.
+     *
+     * @param builder must not have a peerPublicValue set
+     * @since 0.9.16
+     */
+    void returnUnused(DHSessionKeyBuilder builder) {
+        _dhFactory.returnUnused(builder);
+    }
+
     /**
      * how long from initial connection attempt (accept() or connect()) until
      * the con must be established to avoid premature close()ing