From b58f30d71a1ce8eaa0a33002b94e019f0aa72fac Mon Sep 17 00:00:00 2001 From: Sagar Sumit Date: Mon, 12 Sep 2022 20:25:54 +0530 Subject: [PATCH 1/5] [HUDI-4687] Avoid setAccessible which breaks strong encapsulation --- hudi-common/pom.xml | 5 + .../common/util/ObjectSizeCalculator.java | 47 ++++----- .../common/util/TestObjectSizeCalculator.java | 95 +++++++++++++++++++ pom.xml | 7 ++ 4 files changed, 132 insertions(+), 22 deletions(-) create mode 100644 hudi-common/src/test/java/org/apache/hudi/common/util/TestObjectSizeCalculator.java diff --git a/hudi-common/pom.xml b/hudi-common/pom.xml index d3f37864e3ee..ae5303cc7072 100644 --- a/hudi-common/pom.xml +++ b/hudi-common/pom.xml @@ -101,6 +101,11 @@ + + org.openjdk.jol + jol-core + + org.apache.logging.log4j diff --git a/hudi-common/src/main/java/org/apache/hudi/common/util/ObjectSizeCalculator.java b/hudi-common/src/main/java/org/apache/hudi/common/util/ObjectSizeCalculator.java index 7e625e8eb478..5d16950abfb8 100644 --- a/hudi-common/src/main/java/org/apache/hudi/common/util/ObjectSizeCalculator.java +++ b/hudi-common/src/main/java/org/apache/hudi/common/util/ObjectSizeCalculator.java @@ -18,14 +18,16 @@ package org.apache.hudi.common.util; -import org.apache.hudi.common.util.jvm.MemoryLayoutSpecification; import org.apache.hudi.common.util.jvm.HotSpotMemoryLayoutSpecification32bit; import org.apache.hudi.common.util.jvm.HotSpotMemoryLayoutSpecification64bit; import org.apache.hudi.common.util.jvm.HotSpotMemoryLayoutSpecification64bitCompressed; +import org.apache.hudi.common.util.jvm.MemoryLayoutSpecification; import org.apache.hudi.common.util.jvm.OpenJ9MemoryLayoutSpecification32bit; import org.apache.hudi.common.util.jvm.OpenJ9MemoryLayoutSpecification64bit; import org.apache.hudi.common.util.jvm.OpenJ9MemoryLayoutSpecification64bitCompressed; +import org.openjdk.jol.info.ClassLayout; + import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.reflect.Array; @@ -87,7 +89,7 @@ public static long getObjectSize(Object obj) throws UnsupportedOperationExceptio // added. private final int superclassFieldPadding; - private final Map, ClassSizeInfo> classSizeInfos = new IdentityHashMap<>(); + private static final Map, ClassSizeInfo> CLASS_SIZE_INFO_MAP = new IdentityHashMap<>(); private final Set alreadyVisited = Collections.newSetFromMap(new IdentityHashMap<>()); private final Deque pending = new ArrayDeque<>(64); @@ -127,6 +129,8 @@ public synchronized long calculateObjectSize(Object obj) { } obj = pending.removeFirst(); } + } catch (IllegalAccessException | SecurityException e) { + return ClassLayout.parseClass(obj.getClass()).instanceSize(); } finally { alreadyVisited.clear(); pending.clear(); @@ -134,16 +138,20 @@ public synchronized long calculateObjectSize(Object obj) { } } - private ClassSizeInfo getClassSizeInfo(final Class clazz) { - ClassSizeInfo csi = classSizeInfos.get(clazz); + private static ClassSizeInfo getClassSizeInfo(final Class clazz, + int referenceSize, + int superclassFieldPadding, + int objectHeaderSize, + int objectPadding) { + ClassSizeInfo csi = CLASS_SIZE_INFO_MAP.get(clazz); if (csi == null) { - csi = new ClassSizeInfo(clazz); - classSizeInfos.put(clazz, csi); + csi = new ClassSizeInfo(clazz, referenceSize, superclassFieldPadding, objectHeaderSize, objectPadding); + CLASS_SIZE_INFO_MAP.put(clazz, csi); } return csi; } - private void visit(Object obj) { + private void visit(Object obj) throws IllegalAccessException { if (alreadyVisited.contains(obj)) { return; } @@ -155,7 +163,7 @@ private void visit(Object obj) { if (clazz.isArray()) { visitArray(obj); } else { - getClassSizeInfo(clazz).visit(obj, this); + getClassSizeInfo(clazz, referenceSize, superclassFieldPadding, objectHeaderSize, objectPadding).visit(obj, this); } } } @@ -200,7 +208,7 @@ private static class ArrayElementsVisitor { this.array = array; } - public void visit(ObjectSizeCalculator calc) { + public void visit(ObjectSizeCalculator calc) throws IllegalAccessException { for (Object elem : array) { if (elem != null) { calc.visit(elem); @@ -223,7 +231,7 @@ static long roundTo(long x, int multiple) { return ((x + multiple - 1) / multiple) * multiple; } - private class ClassSizeInfo { + private static class ClassSizeInfo { // Padded fields + header size private final long objectSize; @@ -232,7 +240,7 @@ private class ClassSizeInfo { private final long fieldsSize; private final Field[] referenceFields; - public ClassSizeInfo(Class clazz) { + public ClassSizeInfo(Class clazz, int referenceSize, int superclassFieldPadding, int objectHeaderSize, int objectPadding) { long fieldsSize = 0; final List referenceFields = new LinkedList<>(); for (Field f : clazz.getDeclaredFields()) { @@ -243,14 +251,13 @@ public ClassSizeInfo(Class clazz) { if (type.isPrimitive()) { fieldsSize += getPrimitiveFieldSize(type); } else { - f.setAccessible(true); referenceFields.add(f); fieldsSize += referenceSize; } } final Class superClass = clazz.getSuperclass(); if (superClass != null) { - final ClassSizeInfo superClassInfo = getClassSizeInfo(superClass); + final ClassSizeInfo superClassInfo = getClassSizeInfo(superClass, referenceSize, superclassFieldPadding, objectHeaderSize, objectPadding); fieldsSize += roundTo(superClassInfo.fieldsSize, superclassFieldPadding); referenceFields.addAll(Arrays.asList(superClassInfo.referenceFields)); } @@ -259,18 +266,14 @@ public ClassSizeInfo(Class clazz) { this.referenceFields = referenceFields.toArray(new Field[referenceFields.size()]); } - void visit(Object obj, ObjectSizeCalculator calc) { + void visit(Object obj, ObjectSizeCalculator calc) throws IllegalAccessException { calc.increaseSize(objectSize); enqueueReferencedObjects(obj, calc); } - public void enqueueReferencedObjects(Object obj, ObjectSizeCalculator calc) { + public void enqueueReferencedObjects(Object obj, ObjectSizeCalculator calc) throws IllegalAccessException { for (Field f : referenceFields) { - try { - calc.enqueue(f.get(obj)); - } catch (IllegalAccessException e) { - throw new AssertionError("Unexpected denial of access to " + f, e); - } + calc.enqueue(f.get(obj)); } } } @@ -307,7 +310,7 @@ static MemoryLayoutSpecification getEffectiveMemoryLayoutSpecification() { return new OpenJ9MemoryLayoutSpecification32bit(); } else if (!"64".equals(dataModel)) { throw new UnsupportedOperationException( - "Unrecognized value '" + dataModel + "' of sun.arch.data.model system property"); + "Unrecognized value '" + dataModel + "' of sun.arch.data.model system property"); } long maxMemory = 0; @@ -329,7 +332,7 @@ static MemoryLayoutSpecification getEffectiveMemoryLayoutSpecification() { return new HotSpotMemoryLayoutSpecification32bit(); } else if (!"64".equals(dataModel)) { throw new UnsupportedOperationException( - "Unrecognized value '" + dataModel + "' of sun.arch.data.model system property"); + "Unrecognized value '" + dataModel + "' of sun.arch.data.model system property"); } final int vmVersion = Integer.parseInt(strVmVersion.substring(0, strVmVersion.indexOf('.'))); diff --git a/hudi-common/src/test/java/org/apache/hudi/common/util/TestObjectSizeCalculator.java b/hudi-common/src/test/java/org/apache/hudi/common/util/TestObjectSizeCalculator.java new file mode 100644 index 000000000000..3d1ecfab9607 --- /dev/null +++ b/hudi-common/src/test/java/org/apache/hudi/common/util/TestObjectSizeCalculator.java @@ -0,0 +1,95 @@ +/* + * 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.hudi.common.util; + +import org.apache.hudi.common.model.HoodieRecord; + +import org.apache.avro.Schema; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class TestObjectSizeCalculator { + + @Test + public void testGetObjectSize() { + EmptyClass emptyClass = new EmptyClass(); + StringClass stringClass = new StringClass(); + PayloadClass payloadClass = new PayloadClass(); + String emptyString = ""; + String string = "hello"; + String[] stringArray = {emptyString, string, " world"}; + String[] anotherStringArray = new String[100]; + List stringList = new ArrayList<>(); + StringBuilder stringBuilder = new StringBuilder(100); + int maxIntPrimitive = Integer.MAX_VALUE; + int minIntPrimitive = Integer.MIN_VALUE; + Integer maxInteger = Integer.MAX_VALUE; + Integer minInteger = Integer.MIN_VALUE; + long zeroLong = 0L; + double zeroDouble = 0.0; + boolean booleanField = true; + Object object = new Object(); + + Assertions.assertDoesNotThrow(() -> { + printObjectSize(emptyString); + printObjectSize(string); + printObjectSize(stringArray); + printObjectSize(anotherStringArray); + printObjectSize(stringList); + printObjectSize(stringBuilder); + printObjectSize(maxIntPrimitive); + printObjectSize(minIntPrimitive); + printObjectSize(maxInteger); + printObjectSize(minInteger); + printObjectSize(zeroLong); + printObjectSize(zeroDouble); + printObjectSize(booleanField); + printObjectSize(DayOfWeek.TUESDAY); + printObjectSize(object); + printObjectSize(emptyClass); + printObjectSize(stringClass); + printObjectSize(payloadClass); + printObjectSize(Schema.create(Schema.Type.STRING)); + }); + } + + public static void printObjectSize(Object object) { + System.out.println("Object type: " + object.getClass() + ", size: " + ObjectSizeCalculator.getObjectSize(object) + " bytes"); + } + + class EmptyClass { + } + + class StringClass { + private String s; + } + + class PayloadClass implements Serializable { + private HoodieRecord record; + } + + public enum DayOfWeek { + MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY + } +} diff --git a/pom.xml b/pom.xml index 767d046ee5b4..a9cc0920f621 100644 --- a/pom.xml +++ b/pom.xml @@ -200,6 +200,7 @@ 3.21.5 1.1.0 3.5.7 + 0.2 8000 http://localhost:${dynamodb-local.port} 2.7.3 @@ -635,6 +636,12 @@ ${scala.collection-compat.version} + + org.openjdk.jol + jol-core + ${openjdk.jol.version} + +