You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2018/11/24 06:38:41 UTC

[3/3] ignite git commit: Revert "IGNITE-602 Fixed possible StackOverflowError in GridToStringBuilder when circular references are present - Fixes #1558."

Revert "IGNITE-602 Fixed possible StackOverflowError in GridToStringBuilder when circular references are present - Fixes #1558."

This reverts commit d67c5bf4c22338695a116e0fbf0a58a4d581af5d.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/307ac58d
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/307ac58d
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/307ac58d

Branch: refs/heads/ignite-2.7
Commit: 307ac58d1aa1f0145c16affe6de96314b1c8ecef
Parents: 5f968d2
Author: Nikolay Izhikov <ni...@apache.org>
Authored: Fri Nov 23 22:52:12 2018 +0300
Committer: Nikolay Izhikov <ni...@apache.org>
Committed: Fri Nov 23 22:52:12 2018 +0300

----------------------------------------------------------------------
 .../util/tostring/GridToStringBuilder.java      | 706 ++++++++++---------
 .../util/tostring/GridToStringThreadLocal.java  |  66 ++
 .../internal/util/tostring/SBLimitedLength.java |  20 -
 .../tostring/GridToStringBuilderSelfTest.java   | 247 +------
 4 files changed, 439 insertions(+), 600 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/307ac58d/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringBuilder.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringBuilder.java b/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringBuilder.java
index 86daf7c..77c333d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringBuilder.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringBuilder.java
@@ -28,16 +28,19 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EventListener;
-import java.util.IdentityHashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -47,9 +50,6 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_
 /**
  * Provides auto-generation framework for {@code toString()} output.
  * <p>
- * In case of recursion, repeatable objects will be shown as "ClassName@hash".
- * But fields will be printed only for the first entry to prevent recursion.
- * <p>
  * Default exclusion policy (can be overridden with {@link GridToStringInclude}
  * annotation):
  * <ul>
@@ -84,9 +84,6 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_INCLUDE_
  */
 public class GridToStringBuilder {
     /** */
-    private static final Object[] EMPTY_ARRAY = new Object[0];
-
-    /** */
     private static final Map<String, GridToStringClassDescriptor> classCache = new ConcurrentHashMap<>();
 
     /** {@link IgniteSystemProperties#IGNITE_TO_STRING_INCLUDE_SENSITIVE} */
@@ -97,30 +94,25 @@ public class GridToStringBuilder {
     private static final int COLLECTION_LIMIT =
         IgniteSystemProperties.getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
 
-    /** Every thread has its own string builder. */
-    private static ThreadLocal<SBLimitedLength> threadLocSB = new ThreadLocal<SBLimitedLength>() {
-        @Override protected SBLimitedLength initialValue() {
-            SBLimitedLength sb = new SBLimitedLength(256);
+    /** */
+    private static ThreadLocal<Queue<GridToStringThreadLocal>> threadCache = new ThreadLocal<Queue<GridToStringThreadLocal>>() {
+        @Override protected Queue<GridToStringThreadLocal> initialValue() {
+            Queue<GridToStringThreadLocal> queue = new LinkedList<>();
 
-            sb.initLimit(new SBLengthLimit());
+            queue.offer(new GridToStringThreadLocal());
 
-            return sb;
+            return queue;
         }
     };
 
-    /**
-     * Contains objects currently printing in the string builder.
-     * <p>
-     * Since {@code toString()} methods can be chain-called from the same thread we
-     * have to keep a map of this objects pointed to the position of previous occurrence
-     * and remove/add them in each {@code toString()} apply.
-     */
-    private static ThreadLocal<IdentityHashMap<Object, Integer>> savedObjects = new ThreadLocal<IdentityHashMap<Object, Integer>>() {
-        @Override protected IdentityHashMap<Object, Integer> initialValue() {
-            return new IdentityHashMap<>();
+    /** */
+    private static ThreadLocal<SBLengthLimit> threadCurLen = new ThreadLocal<SBLengthLimit>() {
+        @Override protected SBLengthLimit initialValue() {
+            return new SBLengthLimit();
         }
     };
 
+
     /**
      * Produces auto-generated output of string presentation for given object and its declaration class.
      *
@@ -269,9 +261,18 @@ public class GridToStringBuilder {
         assert name3 != null;
         assert name4 != null;
 
-        Object[] addNames = new Object[5];
-        Object[] addVals = new Object[5];
-        boolean[] addSens = new boolean[5];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] addNames = tmp.getAdditionalNames();
+        Object[] addVals = tmp.getAdditionalValues();
+        boolean[] addSens = tmp.getAdditionalSensitives();
 
         addNames[0] = name0;
         addVals[0] = val0;
@@ -289,16 +290,20 @@ public class GridToStringBuilder {
         addVals[4] = val4;
         addSens[4] = sens4;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 5);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, addNames, addVals, addSens, 5);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -344,9 +349,18 @@ public class GridToStringBuilder {
         assert name4 != null;
         assert name5 != null;
 
-        Object[] addNames = new Object[6];
-        Object[] addVals = new Object[6];
-        boolean[] addSens = new boolean[6];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] addNames = tmp.getAdditionalNames();
+        Object[] addVals = tmp.getAdditionalValues();
+        boolean[] addSens = tmp.getAdditionalSensitives();
 
         addNames[0] = name0;
         addVals[0] = val0;
@@ -367,16 +381,20 @@ public class GridToStringBuilder {
         addVals[5] = val5;
         addSens[5] = sens5;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 6);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, addNames, addVals, addSens, 6);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -427,9 +445,18 @@ public class GridToStringBuilder {
         assert name5 != null;
         assert name6 != null;
 
-        Object[] addNames = new Object[7];
-        Object[] addVals = new Object[7];
-        boolean[] addSens = new boolean[7];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] addNames = tmp.getAdditionalNames();
+        Object[] addVals = tmp.getAdditionalValues();
+        boolean[] addSens = tmp.getAdditionalSensitives();
 
         addNames[0] = name0;
         addVals[0] = val0;
@@ -453,16 +480,20 @@ public class GridToStringBuilder {
         addVals[6] = val6;
         addSens[6] = sens6;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 7);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, addNames, addVals, addSens, 7);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -526,9 +557,18 @@ public class GridToStringBuilder {
         assert name2 != null;
         assert name3 != null;
 
-        Object[] addNames = new Object[4];
-        Object[] addVals = new Object[4];
-        boolean[] addSens = new boolean[4];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] addNames = tmp.getAdditionalNames();
+        Object[] addVals = tmp.getAdditionalValues();
+        boolean[] addSens = tmp.getAdditionalSensitives();
 
         addNames[0] = name0;
         addVals[0] = val0;
@@ -543,16 +583,20 @@ public class GridToStringBuilder {
         addVals[3] = val3;
         addSens[3] = sens3;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 4);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, addNames, addVals, addSens, 4);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -608,9 +652,18 @@ public class GridToStringBuilder {
         assert name1 != null;
         assert name2 != null;
 
-        Object[] addNames = new Object[3];
-        Object[] addVals = new Object[3];
-        boolean[] addSens = new boolean[3];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] addNames = tmp.getAdditionalNames();
+        Object[] addVals = tmp.getAdditionalValues();
+        boolean[] addSens = tmp.getAdditionalSensitives();
 
         addNames[0] = name0;
         addVals[0] = val0;
@@ -622,16 +675,20 @@ public class GridToStringBuilder {
         addVals[2] = val2;
         addSens[2] = sens2;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 3);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, addNames, addVals, addSens, 3);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -675,9 +732,18 @@ public class GridToStringBuilder {
         assert name0 != null;
         assert name1 != null;
 
-        Object[] addNames = new Object[2];
-        Object[] addVals = new Object[2];
-        boolean[] addSens = new boolean[2];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] addNames = tmp.getAdditionalNames();
+        Object[] addVals = tmp.getAdditionalValues();
+        boolean[] addSens = tmp.getAdditionalSensitives();
 
         addNames[0] = name0;
         addVals[0] = val0;
@@ -686,16 +752,20 @@ public class GridToStringBuilder {
         addVals[1] = val1;
         addSens[1] = sens1;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 2);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, addNames, addVals, addSens, 2);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -729,24 +799,37 @@ public class GridToStringBuilder {
         assert obj != null;
         assert name != null;
 
-        Object[] addNames = new Object[1];
-        Object[] addVals = new Object[1];
-        boolean[] addSens = new boolean[1];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] addNames = tmp.getAdditionalNames();
+        Object[] addVals = tmp.getAdditionalValues();
+        boolean[] addSens = tmp.getAdditionalSensitives();
 
         addNames[0] = name;
         addVals[0] = val;
         addSens[0] = sens;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, addNames, addVals, addSens, 1);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, addNames, addVals, addSens, 1);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -762,16 +845,30 @@ public class GridToStringBuilder {
         assert cls != null;
         assert obj != null;
 
-        SBLimitedLength sb = threadLocSB.get();
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
 
-        boolean newStr = sb.length() == 0;
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        SBLengthLimit lenLim = threadCurLen.get();
+
+        boolean newStr = false;
 
         try {
-            return toStringImpl(cls, sb, obj, EMPTY_ARRAY, EMPTY_ARRAY, null, 0);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(cls, tmp.getStringBuilder(lenLim), obj, tmp.getAdditionalNames(),
+                tmp.getAdditionalValues(), null, 0);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -789,164 +886,68 @@ public class GridToStringBuilder {
     }
 
     /**
-     * Print value with length limitation.
-     *
+     * Print value with length limitation
      * @param buf buffer to print to.
      * @param val value to print, can be {@code null}.
      */
     private static void toString(SBLimitedLength buf, Object val) {
-        toString(buf, null, val);
-    }
-
-    /**
-     * Print value with length limitation.
-     *
-     * @param buf buffer to print to.
-     * @param cls value class.
-     * @param val value to print.
-     */
-    @SuppressWarnings({"unchecked"})
-    private static void toString(SBLimitedLength buf, Class<?> cls, Object val) {
-        if (val == null) {
+        if (val == null)
             buf.a("null");
-
-            return;
-        }
-
-        if (cls == null)
-            cls = val.getClass();
-
-        if (cls.isPrimitive()) {
-            buf.a(val);
-
-            return;
-        }
-
-        IdentityHashMap<Object, Integer> svdObjs = savedObjects.get();
-
-        if (handleRecursion(buf, val, svdObjs))
-            return;
-
-        svdObjs.put(val, buf.length());
-
-        try {
-            if (cls.isArray())
-                addArray(buf, cls, val);
-            else if (val instanceof Collection)
-                addCollection(buf, (Collection) val);
-            else if (val instanceof Map)
-                addMap(buf, (Map<?, ?>) val);
-            else
-                buf.a(val);
-        }
-        finally {
-            svdObjs.remove(val);
-        }
-    }
-
-    /**
-     * Writes array to buffer.
-     *
-     * @param buf String builder buffer.
-     * @param arrType Type of the array.
-     * @param obj Array object.
-     */
-    private static void addArray(SBLimitedLength buf, Class arrType, Object obj) {
-        if (arrType.getComponentType().isPrimitive()) {
-            buf.a(arrayToString(arrType, obj));
-
-            return;
-        }
-
-        Object[] arr = (Object[]) obj;
-
-        buf.a(arrType.getSimpleName()).a(" [");
-
-        for (int i = 0; i < arr.length; i++) {
-            toString(buf, arr[i]);
-
-            if (i == COLLECTION_LIMIT - 1 || i == arr.length - 1)
-                break;
-
-            buf.a(", ");
-        }
-
-        handleOverflow(buf, arr.length);
-
-        buf.a(']');
+        else
+            toString(buf, val.getClass(), val);
     }
 
     /**
-     * Writes collection to buffer.
-     *
-     * @param buf String builder buffer.
-     * @param col Collection object.
+     * Print value with length limitation
+     * @param buf buffer to print to.
+     * @param valClass value class.
+     * @param val value to print
      */
-    private static void addCollection(SBLimitedLength buf, Collection col) {
-        buf.a(col.getClass().getSimpleName()).a(" [");
-
-        int cnt = 0;
-
-        for (Object obj : col) {
-            toString(buf, obj);
+    private static void toString(SBLimitedLength buf, Class<?> valClass, Object val) {
+        if (valClass.isArray())
+            buf.a(arrayToString(valClass, val));
+        else {
+            int overflow = 0;
+            char bracket = ' ';
 
-            if (++cnt == COLLECTION_LIMIT || cnt == col.size())
-                break;
+            if (val instanceof Collection && ((Collection)val).size() > COLLECTION_LIMIT) {
+                overflow = ((Collection)val).size() - COLLECTION_LIMIT;
+                bracket = ']';
+                val = F.retain((Collection) val, true, COLLECTION_LIMIT);
+            }
+            else if (val instanceof Map && ((Map)val).size() > COLLECTION_LIMIT) {
+                Map<Object, Object> tmp = U.newHashMap(COLLECTION_LIMIT);
 
-            buf.a(", ");
-        }
+                overflow = ((Map)val).size() - COLLECTION_LIMIT;
 
-        handleOverflow(buf, col.size());
+                bracket= '}';
 
-        buf.a(']');
-    }
-
-    /**
-     * Writes map to buffer.
-     *
-     * @param buf String builder buffer.
-     * @param map Map object.
-     */
-    private static <K, V> void addMap(SBLimitedLength buf, Map<K, V> map) {
-        buf.a(map.getClass().getSimpleName()).a(" {");
+                int cntr = 0;
 
-        int cnt = 0;
+                for (Object o : ((Map)val).entrySet()) {
+                    Map.Entry e = (Map.Entry)o;
 
-        for (Map.Entry<K, V> e : map.entrySet()) {
-            toString(buf, e.getKey());
+                    tmp.put(e.getKey(), e.getValue());
 
-            buf.a('=');
+                    if (++cntr >= COLLECTION_LIMIT)
+                        break;
+                }
 
-            toString(buf, e.getValue());
+                val = tmp;
+            }
 
-            if (++cnt == COLLECTION_LIMIT || cnt == map.size())
-                break;
+            buf.a(val);
 
-            buf.a(", ");
+            if (overflow > 0) {
+                buf.d(buf.length() - 1);
+                buf.a("... and ").a(overflow).a(" more").a(bracket);
+            }
         }
-
-        handleOverflow(buf, map.size());
-
-        buf.a('}');
-    }
-
-    /**
-     * Writes overflow message to buffer if needed.
-     *
-     * @param buf String builder buffer.
-     * @param size Size to compare with limit.
-     */
-    private static void handleOverflow(SBLimitedLength buf, int size) {
-        int overflow = size - COLLECTION_LIMIT;
-
-        if (overflow > 0)
-            buf.a("... and ").a(overflow).a(" more");
     }
 
     /**
      * Creates an uniformed string presentation for the given object.
      *
-     * @param <T> Type of object.
      * @param cls Class of the object.
      * @param buf String builder buffer.
      * @param obj Object for which to get string presentation.
@@ -955,7 +956,9 @@ public class GridToStringBuilder {
      * @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
      * @param addLen How many additional values will be included.
      * @return String presentation of the given object.
+     * @param <T> Type of object.
      */
+    @SuppressWarnings({"unchecked"})
     private static <T> String toStringImpl(
         Class<T> cls,
         SBLimitedLength buf,
@@ -972,55 +975,13 @@ public class GridToStringBuilder {
         assert addNames.length == addVals.length;
         assert addLen <= addNames.length;
 
-        boolean newStr = buf.length() == 0;
-
-        IdentityHashMap<Object, Integer> svdObjs = savedObjects.get();
-
-        if (newStr)
-            svdObjs.put(obj, buf.length());
-
-        try {
-            String s = toStringImpl0(cls, buf, obj, addNames, addVals, addSens, addLen);
-
-            if (newStr)
-                return s;
-
-            return "";
-        }
-        finally {
-            if (newStr)
-                svdObjs.remove(obj);
-        }
-    }
-
-    /**
-     * Creates an uniformed string presentation for the given object.
-     *
-     * @param cls Class of the object.
-     * @param buf String builder buffer.
-     * @param obj Object for which to get string presentation.
-     * @param addNames Names of additional values to be included.
-     * @param addVals Additional values to be included.
-     * @param addSens Sensitive flag of values or {@code null} if all values are not sensitive.
-     * @param addLen How many additional values will be included.
-     * @return String presentation of the given object.
-     * @param <T> Type of object.
-     */
-    @SuppressWarnings({"unchecked"})
-    private static <T> String toStringImpl0(
-        Class<T> cls,
-        SBLimitedLength buf,
-        T obj,
-        Object[] addNames,
-        Object[] addVals,
-        @Nullable boolean[] addSens,
-        int addLen
-    ) {
         try {
             GridToStringClassDescriptor cd = getClassDescriptor(cls);
 
             assert cd != null;
 
+            buf.setLength(0);
+
             buf.a(cd.getSimpleClassName()).a(" [");
 
             boolean first = true;
@@ -1183,24 +1144,37 @@ public class GridToStringBuilder {
     public static String toString(String str, String name, @Nullable Object val, boolean sens) {
         assert name != null;
 
-        Object[] propNames = new Object[1];
-        Object[] propVals = new Object[1];
-        boolean[] propSens = new boolean[1];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] propNames = tmp.getAdditionalNames();
+        Object[] propVals = tmp.getAdditionalValues();
+        boolean[] propSens = tmp.getAdditionalSensitives();
 
         propNames[0] = name;
         propVals[0] = val;
         propSens[0] = sens;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(str, sb, propNames, propVals, propSens, 1);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(str, tmp.getStringBuilder(lenLim), propNames, propVals, propSens, 1);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -1237,9 +1211,18 @@ public class GridToStringBuilder {
         assert name0 != null;
         assert name1 != null;
 
-        Object[] propNames = new Object[2];
-        Object[] propVals = new Object[2];
-        boolean[] propSens = new boolean[2];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] propNames = tmp.getAdditionalNames();
+        Object[] propVals = tmp.getAdditionalValues();
+        boolean[] propSens = tmp.getAdditionalSensitives();
 
         propNames[0] = name0;
         propVals[0] = val0;
@@ -1248,16 +1231,20 @@ public class GridToStringBuilder {
         propVals[1] = val1;
         propSens[1] = sens1;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(str, sb, propNames, propVals, propSens, 2);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(str, tmp.getStringBuilder(lenLim), propNames, propVals, propSens, 2);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -1284,9 +1271,18 @@ public class GridToStringBuilder {
         assert name1 != null;
         assert name2 != null;
 
-        Object[] propNames = new Object[3];
-        Object[] propVals = new Object[3];
-        boolean[] propSens = new boolean[3];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] propNames = tmp.getAdditionalNames();
+        Object[] propVals = tmp.getAdditionalValues();
+        boolean[] propSens = tmp.getAdditionalSensitives();
 
         propNames[0] = name0;
         propVals[0] = val0;
@@ -1298,16 +1294,20 @@ public class GridToStringBuilder {
         propVals[2] = val2;
         propSens[2] = sens2;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(str, sb, propNames, propVals, propSens, 3);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(str, tmp.getStringBuilder(lenLim), propNames, propVals, propSens, 3);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -1339,9 +1339,18 @@ public class GridToStringBuilder {
         assert name2 != null;
         assert name3 != null;
 
-        Object[] propNames = new Object[4];
-        Object[] propVals = new Object[4];
-        boolean[] propSens = new boolean[4];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] propNames = tmp.getAdditionalNames();
+        Object[] propVals = tmp.getAdditionalValues();
+        boolean[] propSens = tmp.getAdditionalSensitives();
 
         propNames[0] = name0;
         propVals[0] = val0;
@@ -1356,16 +1365,20 @@ public class GridToStringBuilder {
         propVals[3] = val3;
         propSens[3] = sens3;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(str, sb, propNames, propVals, propSens, 4);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(str, tmp.getStringBuilder(lenLim), propNames, propVals, propSens, 4);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -1402,9 +1415,18 @@ public class GridToStringBuilder {
         assert name3 != null;
         assert name4 != null;
 
-        Object[] propNames = new Object[5];
-        Object[] propVals = new Object[5];
-        boolean[] propSens = new boolean[5];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] propNames = tmp.getAdditionalNames();
+        Object[] propVals = tmp.getAdditionalValues();
+        boolean[] propSens = tmp.getAdditionalSensitives();
 
         propNames[0] = name0;
         propVals[0] = val0;
@@ -1422,16 +1444,20 @@ public class GridToStringBuilder {
         propVals[4] = val4;
         propSens[4] = sens4;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(str, sb, propNames, propVals, propSens, 5);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(str, tmp.getStringBuilder(lenLim), propNames, propVals, propSens, 5);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -1473,9 +1499,18 @@ public class GridToStringBuilder {
         assert name4 != null;
         assert name5 != null;
 
-        Object[] propNames = new Object[6];
-        Object[] propVals = new Object[6];
-        boolean[] propSens = new boolean[6];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] propNames = tmp.getAdditionalNames();
+        Object[] propVals = tmp.getAdditionalValues();
+        boolean[] propSens = tmp.getAdditionalSensitives();
 
         propNames[0] = name0;
         propVals[0] = val0;
@@ -1496,16 +1531,20 @@ public class GridToStringBuilder {
         propVals[5] = val5;
         propSens[5] = sens5;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(str, sb, propNames, propVals, propSens, 6);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(str, tmp.getStringBuilder(lenLim), propNames, propVals, propSens, 6);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -1552,9 +1591,18 @@ public class GridToStringBuilder {
         assert name5 != null;
         assert name6 != null;
 
-        Object[] propNames = new Object[7];
-        Object[] propVals = new Object[7];
-        boolean[] propSens = new boolean[7];
+        Queue<GridToStringThreadLocal> queue = threadCache.get();
+
+        assert queue != null;
+
+        // Since string() methods can be chain-called from the same thread we
+        // have to keep a list of thread-local objects and remove/add them
+        // in each string() apply.
+        GridToStringThreadLocal tmp = queue.isEmpty() ? new GridToStringThreadLocal() : queue.remove();
+
+        Object[] propNames = tmp.getAdditionalNames();
+        Object[] propVals = tmp.getAdditionalValues();
+        boolean[] propSens = tmp.getAdditionalSensitives();
 
         propNames[0] = name0;
         propVals[0] = val0;
@@ -1578,16 +1626,20 @@ public class GridToStringBuilder {
         propVals[6] = val6;
         propSens[6] = sens6;
 
-        SBLimitedLength sb = threadLocSB.get();
+        SBLengthLimit lenLim = threadCurLen.get();
 
-        boolean newStr = sb.length() == 0;
+        boolean newStr = false;
 
         try {
-            return toStringImpl(str, sb, propNames, propVals, propSens, 7);
+            newStr = lenLim.length() == 0;
+
+            return toStringImpl(str, tmp.getStringBuilder(lenLim), propNames, propVals, propSens, 7);
         }
         finally {
+            queue.offer(tmp);
+
             if (newStr)
-                sb.reset();
+                lenLim.reset();
         }
     }
 
@@ -1789,54 +1841,4 @@ public class GridToStringBuilder {
 
         return sb.toString();
     }
-
-    /**
-     * Checks that object is already saved.
-     * In positive case this method inserts hash to the saved object entry (if needed) and name@hash for current entry.
-     * Further toString operations are not needed for current object.
-     *
-     * @param buf String builder buffer.
-     * @param obj Object.
-     * @param svdObjs Map with saved objects to handle recursion.
-     * @return {@code True} if object is already saved and name@hash was added to buffer.
-     * {@code False} if it wasn't saved previously and it should be saved.
-     */
-    private static boolean handleRecursion(SBLimitedLength buf, Object obj, IdentityHashMap<Object, Integer> svdObjs) {
-        Integer pos = svdObjs.get(obj);
-
-        if (pos == null)
-            return false;
-
-        String name = obj.getClass().getSimpleName();
-        String hash = '@' + Integer.toHexString(System.identityHashCode(obj));
-        String savedName = name + hash;
-
-        if (!buf.isOverflowed() && buf.impl().indexOf(savedName, pos) != pos) {
-            buf.i(pos + name.length(), hash);
-
-            incValues(svdObjs, obj, hash.length());
-        }
-
-        buf.a(savedName);
-
-        return true;
-    }
-
-    /**
-     * Increment positions of already presented objects afterward given object.
-     *
-     * @param svdObjs Map with objects already presented in the buffer.
-     * @param obj Object.
-     * @param hashLen Length of the object's hash.
-     */
-    private static void incValues(IdentityHashMap<Object, Integer> svdObjs, Object obj, int hashLen) {
-        Integer baseline = svdObjs.get(obj);
-
-        for (IdentityHashMap.Entry<Object, Integer> entry : svdObjs.entrySet()) {
-            Integer pos = entry.getValue();
-
-            if (pos > baseline)
-                entry.setValue(pos + hashLen);
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/307ac58d/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringThreadLocal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringThreadLocal.java b/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringThreadLocal.java
new file mode 100644
index 0000000..2f62727
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/GridToStringThreadLocal.java
@@ -0,0 +1,66 @@
+/*
+ * 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.ignite.internal.util.tostring;
+
+/**
+ * Helper wrapper containing StringBuilder and additional values. Stored as a thread-local variable.
+ */
+class GridToStringThreadLocal {
+    /** */
+    private SBLimitedLength sb = new SBLimitedLength(256);
+
+    /** */
+    private Object[] addNames = new Object[7];
+
+    /** */
+    private Object[] addVals = new Object[7];
+
+    /** */
+    private boolean[] addSens = new boolean[7];
+
+    /**
+     * @param len Length limit.
+     * @return String builder.
+     */
+    SBLimitedLength getStringBuilder(SBLengthLimit len) {
+        sb.initLimit(len);
+
+        return sb;
+    }
+
+    /**
+     * @return Additional names.
+     */
+    Object[] getAdditionalNames() {
+        return addNames;
+    }
+
+    /**
+     * @return Additional values.
+     */
+    Object[] getAdditionalValues() {
+        return addVals;
+    }
+
+    /**
+     * @return Additional values.
+     */
+    boolean[] getAdditionalSensitives() {
+        return addSens;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/307ac58d/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/SBLimitedLength.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/SBLimitedLength.java b/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/SBLimitedLength.java
index b524b3d..c47cf40 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/SBLimitedLength.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/tostring/SBLimitedLength.java
@@ -51,19 +51,6 @@ public class SBLimitedLength extends GridStringBuilder {
     }
 
     /**
-     * Resets buffer.
-     */
-    public void reset() {
-        super.setLength(0);
-
-        lenLimit.reset();
-
-        if (tail != null)
-            tail.reset();
-    }
-
-
-    /**
      * @return tail string builder.
      */
     public CircularStringBuilder getTail() {
@@ -305,11 +292,4 @@ public class SBLimitedLength extends GridStringBuilder {
             return res.toString();
         }
     }
-
-    /**
-     * @return {@code True} - if buffer limit is reached.
-     */
-    public boolean isOverflowed() {
-        return lenLimit.overflowed(this);
-    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/307ac58d/modules/core/src/test/java/org/apache/ignite/internal/util/tostring/GridToStringBuilderSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/tostring/GridToStringBuilderSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/tostring/GridToStringBuilderSelfTest.java
index eff3349..4ac05fb 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/tostring/GridToStringBuilderSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/tostring/GridToStringBuilderSelfTest.java
@@ -20,25 +20,19 @@ package org.apache.ignite.internal.util.tostring;
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.UUID;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.locks.ReadWriteLock;
 import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.IgniteSystemProperties;
-import org.apache.ignite.internal.IgniteInternalFuture;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_COLLECTION_LIMIT;
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_MAX_LENGTH;
 import org.apache.ignite.internal.util.typedef.internal.S;
-import org.apache.ignite.testframework.GridTestUtils;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.apache.ignite.testframework.junits.common.GridCommonTest;
 
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_COLLECTION_LIMIT;
-import static org.apache.ignite.IgniteSystemProperties.IGNITE_TO_STRING_MAX_LENGTH;
-
 /**
  * Tests for {@link GridToStringBuilder}.
  */
@@ -78,164 +72,32 @@ public class GridToStringBuilderSelfTest extends GridCommonAbstractTest {
     /**
      * @throws Exception If failed.
      */
-    public void testToStringCheckSimpleListRecursionPrevention() throws Exception {
+    public void testToStringCheckSimpleRecursionPrevention() throws Exception {
         ArrayList<Object> list1 = new ArrayList<>();
         ArrayList<Object> list2 = new ArrayList<>();
 
         list2.add(list1);
         list1.add(list2);
 
-        info(GridToStringBuilder.toString(ArrayList.class, list1));
-        info(GridToStringBuilder.toString(ArrayList.class, list2));
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testToStringCheckSimpleMapRecursionPrevention() throws Exception {
-        HashMap<Object, Object> map1 = new HashMap<>();
-        HashMap<Object, Object> map2 = new HashMap<>();
-
-        map1.put("2", map2);
-        map2.put("1", map1);
 
-        info(GridToStringBuilder.toString(HashMap.class, map1));
-        info(GridToStringBuilder.toString(HashMap.class, map2));
+        GridToStringBuilder.toString(ArrayList.class, list1);
+        GridToStringBuilder.toString(ArrayList.class, list2);
     }
 
     /**
      * @throws Exception If failed.
      */
-    public void testToStringCheckListAdvancedRecursionPrevention() throws Exception {
+    public void testToStringCheckAdvancedRecursionPrevention() throws Exception {
+        fail("https://issues.apache.org/jira/browse/IGNITE-602");
+
         ArrayList<Object> list1 = new ArrayList<>();
         ArrayList<Object> list2 = new ArrayList<>();
 
         list2.add(list1);
         list1.add(list2);
 
-        info(GridToStringBuilder.toString(ArrayList.class, list1, "name", list2));
-        info(GridToStringBuilder.toString(ArrayList.class, list2, "name", list1));
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testToStringCheckMapAdvancedRecursionPrevention() throws Exception {
-        HashMap<Object, Object> map1 = new HashMap<>();
-        HashMap<Object, Object> map2 = new HashMap<>();
-
-        map1.put("2", map2);
-        map2.put("1", map1);
-
-        info(GridToStringBuilder.toString(HashMap.class, map1, "name", map2));
-        info(GridToStringBuilder.toString(HashMap.class, map2, "name", map1));
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testToStringCheckObjectRecursionPrevention() throws Exception {
-        Node n1 = new Node();
-        Node n2 = new Node();
-        Node n3 = new Node();
-        Node n4 = new Node();
-
-        n1.name = "n1";
-        n2.name = "n2";
-        n3.name = "n3";
-        n4.name = "n4";
-
-        n1.next = n2;
-        n2.next = n3;
-        n3.next = n4;
-        n4.next = n3;
-
-        n1.nodes = new Node[4];
-        n2.nodes = n1.nodes;
-        n3.nodes = n1.nodes;
-        n4.nodes = n1.nodes;
-
-        n1.nodes[0] = n1;
-        n1.nodes[1] = n2;
-        n1.nodes[2] = n3;
-        n1.nodes[3] = n4;
-
-        String expN1 = n1.toString();
-        String expN2 = n2.toString();
-        String expN3 = n3.toString();
-        String expN4 = n4.toString();
-
-        info(expN1);
-        info(expN2);
-        info(expN3);
-        info(expN4);
-        info(GridToStringBuilder.toString("Test", "Appended vals", n1));
-
-        CyclicBarrier bar = new CyclicBarrier(4);
-
-        IgniteInternalFuture<String> fut1 = GridTestUtils.runAsync(new BarrierCallable(bar, n1, expN1));
-        IgniteInternalFuture<String> fut2 = GridTestUtils.runAsync(new BarrierCallable(bar, n2, expN2));
-        IgniteInternalFuture<String> fut3 = GridTestUtils.runAsync(new BarrierCallable(bar, n3, expN3));
-        IgniteInternalFuture<String> fut4 = GridTestUtils.runAsync(new BarrierCallable(bar, n4, expN4));
-
-        fut1.get(3_000);
-        fut2.get(3_000);
-        fut3.get(3_000);
-        fut4.get(3_000);
-    }
-
-    /**
-     * Test class.
-     */
-    private static class Node {
-        /** */
-        @GridToStringInclude
-        String name;
-
-        /** */
-        @GridToStringInclude
-        Node next;
-
-        /** */
-        @GridToStringInclude
-        Node[] nodes;
-
-        /** {@inheritDoc} */
-        @Override public String toString() {
-            return GridToStringBuilder.toString(Node.class, this);
-        }
-    }
-
-    /**
-     * Test class.
-     */
-    private static class BarrierCallable implements Callable<String> {
-        /** */
-        CyclicBarrier bar;
-
-        /** */
-        Object obj;
-
-        /** Expected value of {@code toString()} method. */
-        String exp;
-
-        /** */
-        private BarrierCallable(CyclicBarrier bar, Object obj, String exp) {
-            this.bar = bar;
-            this.obj = obj;
-            this.exp = exp;
-        }
-
-        /** {@inheritDoc} */
-        @Override public String call() throws Exception {
-            for (int i = 0; i < 10; i++) {
-                bar.await();
-
-                assertEquals(exp, obj.toString());
-            }
-
-            return null;
-        }
+        GridToStringBuilder.toString(ArrayList.class, list1, "name", list2);
+        GridToStringBuilder.toString(ArrayList.class, list2, "name", list1);
     }
 
     /**
@@ -275,33 +137,6 @@ public class GridToStringBuilderSelfTest extends GridCommonAbstractTest {
         Arrays.fill(arrOf, v);
         T[] arr = Arrays.copyOf(arrOf, limit);
 
-        checkArrayOverflow(arrOf, arr, limit);
-    }
-
-    /**
-     * Test array print.
-     *
-     * @throws Exception if failed.
-     */
-    public void testArrLimitWithRecursion() throws Exception {
-        int limit = IgniteSystemProperties.getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
-
-        ArrayList[] arrOf = new ArrayList[limit + 1];
-        Arrays.fill(arrOf, new ArrayList());
-        ArrayList[] arr = Arrays.copyOf(arrOf, limit);
-
-        arrOf[0].add(arrOf);
-        arr[0].add(arr);
-
-        checkArrayOverflow(arrOf, arr, limit);
-    }
-
-    /**
-     * @param arrOf Array.
-     * @param arr Array copy.
-     * @param limit Array limit.
-     */
-    private void checkArrayOverflow(Object[] arrOf, Object[] arr, int limit) {
         String arrStr = GridToStringBuilder.arrayToString(arr.getClass(), arr);
         String arrOfStr = GridToStringBuilder.arrayToString(arrOf.getClass(), arrOf);
 
@@ -309,11 +144,7 @@ public class GridToStringBuilderSelfTest extends GridCommonAbstractTest {
         StringBuilder resultSB = new StringBuilder(arrStr);
             resultSB.deleteCharAt(resultSB.length()-1);
             resultSB.append("... and ").append(arrOf.length - limit).append(" more]");
-
-        arrStr = resultSB.toString();
-
-        info(arrOfStr);
-        info(arrStr);
+            arrStr = resultSB.toString();
 
         assertTrue("Collection limit error in array of type " + arrOf.getClass().getName()
             + " error, normal arr: <" + arrStr + ">, overflowed arr: <" + arrOfStr + ">", arrStr.equals(arrOfStr));
@@ -397,58 +228,18 @@ public class GridToStringBuilderSelfTest extends GridCommonAbstractTest {
             strMap.put("k" + i, "v");
             strList.add("e");
         }
+        String testClassStr = GridToStringBuilder.toString(TestClass1.class, testClass);
 
-        checkColAndMap(testClass);
-    }
-
-    /**
-     * @throws Exception If failed.
-     */
-    public void testToStringColAndMapLimitWithRecursion() throws Exception {
-        int limit = IgniteSystemProperties.getInteger(IGNITE_TO_STRING_COLLECTION_LIMIT, 100);
-        Map strMap = new TreeMap<>();
-        List strList = new ArrayList<>(limit+1);
-
-        TestClass1 testClass = new TestClass1();
-        testClass.strMap = strMap;
-        testClass.strListIncl = strList;
-
-        Map m = new TreeMap();
-        m.put("m", strMap);
-
-        List l = new ArrayList();
-        l.add(strList);
-
-        strMap.put("k0", m);
-        strList.add(l);
-
-        for (int i = 1; i < limit; i++) {
-            strMap.put("k" + i, "v");
-            strList.add("e");
-        }
-
-        checkColAndMap(testClass);
-    }
-
-    /**
-     * @param testCls Class with collection and map included in toString().
-     */
-    private void checkColAndMap(TestClass1 testCls) {
-        String testClsStr = GridToStringBuilder.toString(TestClass1.class, testCls);
-
-        testCls.strMap.put("kz", "v"); // important to add last element in TreeMap here
-        testCls.strListIncl.add("e");
+        strMap.put("kz", "v"); // important to add last element in TreeMap here
+        strList.add("e");
 
-        String testClsStrOf = GridToStringBuilder.toString(TestClass1.class, testCls);
+        String testClassStrOf = GridToStringBuilder.toString(TestClass1.class, testClass);
 
-        String testClsStrOfR = testClsStrOf.replaceAll("... and 1 more","");
+        String testClassStrOfR = testClassStrOf.replaceAll("... and 1 more","");
 
-        info(testClsStr);
-        info(testClsStrOf);
-        info(testClsStrOfR);
+        assertTrue("Collection limit error in Map or List, normal: <" + testClassStr + ">, overflowed: <"
+            +"testClassStrOf", testClassStr.length() == testClassStrOfR.length());
 
-        assertTrue("Collection limit error in Map or List, normal: <" + testClsStr + ">, overflowed: <"
-            + testClsStrOf + ">", testClsStr.length() == testClsStrOfR.length());
     }
 
     /**
@@ -611,4 +402,4 @@ public class GridToStringBuilderSelfTest extends GridCommonAbstractTest {
             this.str = str;
         }
     }
-}
+}
\ No newline at end of file