├── .gitignore ├── LICENSE ├── README.adoc ├── azure-pipelines.yml ├── findbugs-exclude-filter.xml ├── header.txt ├── pom.xml └── src ├── hidden └── resources │ └── META-INF │ └── MANIFEST.MF ├── main └── java │ └── org │ └── ehcache │ └── sizeof │ ├── Configuration.java │ ├── Filter.java │ ├── FilterConfigurator.java │ ├── FlyWeightType.java │ ├── ObjectGraphWalker.java │ ├── SizeOf.java │ ├── SizeOfFilterSource.java │ ├── VisitorListener.java │ ├── annotations │ ├── AnnotationProxyFactory.java │ └── IgnoreSizeOf.java │ ├── filters │ ├── AnnotationSizeOfFilter.java │ ├── CombinationSizeOfFilter.java │ ├── SizeOfFilter.java │ └── TypeFilter.java │ ├── impl │ ├── AgentLoader.java │ ├── AgentSizeOf.java │ ├── JvmInformation.java │ ├── PassThroughFilter.java │ ├── PrimitiveType.java │ ├── ReflectionSizeOf.java │ ├── SizeOfAgent.java │ └── UnsafeSizeOf.java │ └── util │ └── WeakIdentityConcurrentMap.java └── test ├── java ├── com │ └── terracotta │ │ ├── ehcache │ │ └── special │ │ │ └── annotation │ │ │ ├── IgnoreSizeOf.java │ │ │ ├── IgnoreSizeOffff.java │ │ │ └── no │ │ │ └── inherited │ │ │ └── IgnoreSizeOf.java │ │ └── special │ │ └── annotation │ │ └── IgnoreSizeOf.java └── org │ └── ehcache │ └── sizeof │ ├── AbstractSizeOfTest.java │ ├── ConfigurationTest.java │ ├── CrossCheckingSizeOf.java │ ├── CrossCheckingSizeOfIT.java │ ├── FilteredSizeOfTest.java │ ├── ObjectGraphWalkerTest.java │ ├── SizeOfFilterSourceTest.java │ ├── SizeOfTest.java │ ├── SizeOfTestValues.java │ ├── annotations │ ├── AnnotationProxyFactoryTest.java │ ├── CustomAnnotation.java │ ├── ExampleEnum.java │ └── ReferenceAnnotation.java │ ├── filteredtest │ ├── AnnotationFilteredPackage.java │ ├── custom │ │ ├── CustomAnnotationFilteredPackage.java │ │ └── package-info.java │ └── package-info.java │ ├── filters │ └── TypeFilterTest.java │ └── impl │ ├── AgentLoaderRaceTest.java │ ├── AgentLoaderSystemPropTest.java │ ├── AgentLoaderTest.java │ └── JvmInformationTest.java └── resources ├── logback-test.xml └── services └── org.ehcache.sizeof.FilterConfigurator.txt /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | /.idea/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = ehcache-sizeofengine 2 | 3 | == What is this? 4 | 5 | This library lets you size Java Object instances in bytes. 6 | 7 | [source,java,] 8 | ---- 9 | SizeOf sizeOf = SizeOf.newInstance(); // <1> 10 | long shallowSize = sizeOf.sizeOf(someObject); // <2> 11 | long deepSize = sizeOf.deepSizeOf(someObject); // <3> 12 | ---- 13 | <1> Retrieves one of the multiple engines supported; 14 | <2> Sizes `someObject` instance in bytes; 15 | <3> Sizes `someObject` instance in bytes, as well as all objects it references. 16 | 17 | === The different engines 18 | 19 | When retrieving a new `SizeOf` instance with the static `org.ehcache.sizeof.SizeOf.newInstance`, the library will try to create of 20 | the following engines, in that order: 21 | 22 | . `AgentSizeOf` : Which tries to attach an agent to the JVM process and size objects using `org.ehcache.sizeof.impl.AgentLoader.instrumentation`; otherwise 23 | . `UnsafeSizeOf` : Which will determine Class layouts in memory using `sun.misc.Unsafe`; or finally 24 | . `ReflectionSizeOf` : Which will introspect Class instances and try determining object sizes that way. 25 | 26 | Both the `ReflectionSizeOf` and the `AgentSizeOf` approach were very well covered in Dr. Heinz Kabutz's 27 | http://www.javaspecialists.eu/archive/Issue078.html[Java Specialist Newsletter issue #78] and http://www.javaspecialists.eu/archive/Issue142.html[issue #142] respectively. 28 | Different JVMs and their configurations may affect these sizes, see blob/master/src/main/java/org/ehcache/sizeof/impl/JvmInformation.java[JvmInformation enum] for more details. 29 | Finally, the `UnsafeSizeOf`, we've discovered and, as far as we know, were the first ones to use. 30 | 31 | == Avoiding sizing certain types 32 | 33 | In some cases you want to control how far the deep sizing goes. You can do this using the `org.ehcache.sizeof.Filter` SPI. 34 | 35 | === Using existing filter configurators 36 | 37 | You simply need to add the jars of the modules that you want along side this project's jar 38 | 39 | * Hibernate 40 | ** https://github.com/alexsnaps/ehcache-sizeofengine-hibernate[https://github.com/alexsnaps/ehcache-sizeofengine-hibernate v4+] 41 | ** https://github.com/alexsnaps/ehcache-sizeofengine-hibernate3[https://github.com/alexsnaps/ehcache-sizeofengine-hibernate3 v3+] 42 | * Groovy 43 | ** https://github.com/noamt/ehcache-sizeofengine-groovy 44 | 45 | == Configuring the Filter yourself 46 | 47 | In order to ignore fields or instances of certain classes when sizing object graphs, you'll have to 48 | 49 | . Create a http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html[ServiceLoader] project, for http://terracotta-oss.github.io/ehcache-sizeofengine/apidocs/net/sf/ehcache/sizeofengine/FilterConfigurator.html[net.sf.ehcache.sizeofengine.FilterConfigurator] 50 | * Have your jar contain a text file named +META-INF/services/org.ehcache.sizeof.FilterConfigurator+ 51 | * The file should contain the fully qualified class name of your implementation 52 | . Implement http://terracotta-oss.github.io/ehcache-sizeofengine/apidocs/net/sf/ehcache/sizeofengine/FilterConfigurator.html[FilterConfigurator]'s configure method to configure the http://terracotta-oss.github.io/ehcache-sizeofengine/apidocs/net/sf/ehcache/sizeofengine/Filter.html[Filter] of classes and fields 53 | . put your jar on your application's classpath, along side of the ehcache jar and this ehcache-sizeofengine jar 54 | . Use Ehcache's http://ehcache.org/documentation/arc[Automatic Resource Control] for your heap tier 55 | 56 | == Example 57 | 58 | [source,java] 59 | ---- 60 | public static final class StupidConfigurator implements FilterConfigurator { 61 | 62 | @Override 63 | public void configure(final Filter ehcacheFilter) { 64 | // Will not size any instance of Number, and given the second arg, no subtype neither 65 | ehcacheFilter.ignoreInstancesOf(Number.class, false); 66 | } 67 | } 68 | ---- 69 | 70 | There can be as many +FilterConfigurator+ on the classpath as required, they'll have configure the filter once. 71 | The +Filter+ is shared across all +SizeOfEngine+ instances created. 72 | 73 | == Using it 74 | 75 | === Maven 76 | 77 | Releases are available from Maven Central. 78 | 79 | [source,xml] 80 | ---- 81 | 82 | org.ehcache 83 | sizeof 84 | ${sizeof.version} 85 | 86 | ---- 87 | 88 | Snapshots are available from the https://oss.sonatype.org/content/repositories/snapshots[Sonatype OSS snapshots] repository. 89 | In order to access the snapshots, you need to add the following repository to your +pom.xml+: 90 | 91 | [source,xml] 92 | ---- 93 | 94 | sonatype-nexus-snapshots 95 | Sonatype Nexus Snapshots 96 | https://oss.sonatype.org/content/repositories/snapshots 97 | 98 | false 99 | 100 | 101 | true 102 | 103 | 104 | ---- 105 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright Terracotta, Inc. 3 | # Copyright IBM Corp. 2024, 2025 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | # See shared code location for steps and parameters: 19 | # https://dev.azure.com/TerracottaCI/_git/terracotta 20 | 21 | resources: 22 | repositories: 23 | - repository: templates 24 | type: git 25 | name: terracotta/terracotta 26 | 27 | jobs: 28 | - template: build-templates/maven-common.yml@templates 29 | -------------------------------------------------------------------------------- /findbugs-exclude-filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /header.txt: -------------------------------------------------------------------------------- 1 | Copyright Terracotta, Inc. 2 | Copyright IBM Corp. 2024, 2025 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | org.sonatype.oss 9 | oss-parent 10 | 9 11 | 12 | 13 | org.ehcache 14 | sizeof 15 | 0.4.3-SNAPSHOT 16 | jar 17 | 18 | Ehcache SizeOf Engine 19 | SizeOf engine, extracted from Ehcache 20 | 21 | https://github.com/ehcache/sizeof 22 | 23 | 24 | Terracotta 25 | http://terracotta.org 26 | 27 | 28 | 29 | 30 | The Apache Software License, Version 2.0 31 | http://www.apache.org/licenses/LICENSE-2.0.txt 32 | repo 33 | 34 | 35 | 36 | 37 | UTF-8 38 | UTF-8 39 | 1.8 40 | 1.7.32 41 | [${slf4j.base.version},1.7.9999) 42 | 1.2.11 43 | [${logback.base.version},1.2.9999) 44 | 45 | 46 | 47 | 48 | org.slf4j 49 | slf4j-api 50 | ${slf4j.range.version} 51 | 52 | 53 | ch.qos.logback 54 | logback-classic 55 | ${logback.range.version} 56 | test 57 | 58 | 59 | junit 60 | junit 61 | 4.12 62 | test 63 | 64 | 65 | org.hamcrest 66 | hamcrest-library 67 | 1.3 68 | test 69 | 70 | 71 | org.ow2.asm 72 | asm 73 | 6.0 74 | test 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | maven-clean-plugin 83 | 3.0.0 84 | 85 | 86 | maven-compiler-plugin 87 | 3.8.1 88 | 89 | 90 | maven-deploy-plugin 91 | 3.0.0-M1 92 | 93 | 94 | maven-enforcer-plugin 95 | 3.0.0-M1 96 | 97 | 98 | maven-failsafe-plugin 99 | 3.0.0-M5 100 | 101 | 102 | maven-gpg-plugin 103 | 1.6 104 | 105 | 106 | maven-install-plugin 107 | 3.0.0-M1 108 | 109 | 110 | maven-jar-plugin 111 | 3.1.2 112 | 113 | 114 | maven-javadoc-plugin 115 | 3.1.1 116 | 117 | ${skipJavadoc} 118 | 8 119 | 120 | 121 | 122 | maven-resources-plugin 123 | 3.0.1 124 | 125 | 126 | maven-site-plugin 127 | 3.5.1 128 | 129 | 130 | maven-source-plugin 131 | 3.1.0 132 | 133 | 134 | maven-surefire-plugin 135 | 3.0.0-M5 136 | 137 | 138 | 139 | org.codehaus.mojo 140 | findbugs-maven-plugin 141 | 3.0.5 142 | 143 | 144 | org.jacoco 145 | jacoco-maven-plugin 146 | 0.8.3 147 | 148 | 149 | org.apache.rat 150 | apache-rat-plugin 151 | 0.12 152 | 153 | 154 | org.codehaus.gmavenplus 155 | gmavenplus-plugin 156 | 1.6 157 | 158 | 159 | org.codehaus.mojo 160 | versions-maven-plugin 161 | 2.5 162 | 163 | 164 | com.mycila.maven-license-plugin 165 | maven-license-plugin 166 | 1.10.b1 167 | 168 | true 169 |
${project.basedir}/header.txt
170 | 171 | 172 | src/**/*.java 173 | 174 |
175 |
176 |
177 |
178 | 179 | 180 | 181 | maven-compiler-plugin 182 | 183 | ${java.version} 184 | ${java.version} 185 | 186 | 187 | 188 | org.apache.rat 189 | apache-rat-plugin 190 | 191 | 192 | verify 193 | 194 | check 195 | 196 | 197 | 198 | src/hidden/** 199 | README.adoc 200 | **/*.txt 201 | 202 | 203 | 204 | 205 | 206 | 207 | org.codehaus.gmavenplus 208 | gmavenplus-plugin 209 | 210 | 211 | create-agent-jar 212 | process-classes 213 | 214 | execute 215 | 216 | 217 | 218 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | org.codehaus.groovy 237 | groovy-all 238 | 239 | 2.4.12 240 | runtime 241 | 242 | 243 | 244 | 245 | maven-surefire-plugin 246 | 247 | -Xms64m -Xmx64m 248 | false 249 | 250 | 251 | 252 | maven-failsafe-plugin 253 | 254 | 255 | 256 | integration-test 257 | verify 258 | 259 | 260 | 261 | 262 | 263 | org.jacoco 264 | jacoco-maven-plugin 265 | 266 | 267 | prepare-agent 268 | 269 | prepare-agent 270 | 271 | 272 | jacocoArgs 273 | 274 | 275 | 276 | report 277 | verify 278 | 279 | report 280 | 281 | 282 | 283 | 284 | 285 | org.codehaus.mojo 286 | findbugs-maven-plugin 287 | 288 | ${basedir}/findbugs-exclude-filter.xml 289 | 290 | 291 | 292 | 293 | check 294 | 295 | 296 | 297 | 298 | 299 | maven-enforcer-plugin 300 | 301 | 302 | 303 | [3.0.5,) 304 | 305 | 306 | 307 | 308 | 309 | enforce-maven 310 | 311 | enforce 312 | 313 | 314 | 315 | 316 | 317 | maven-source-plugin 318 | 319 | 320 | attach-sources 321 | verify 322 | 323 | jar-no-fork 324 | 325 | 326 | 327 | 328 | 329 | maven-javadoc-plugin 330 | 331 | 332 | attach-javadocs 333 | 334 | jar 335 | 336 | 337 | ${skipJavadoc} 338 | 8 339 | -Xdoclint:none 340 | 341 | 342 | 343 | 344 | 345 | org.sonatype.plugins 346 | nexus-staging-maven-plugin 347 | 1.6.13 348 | true 349 | 350 | 351 | https://oss.sonatype.org 352 | 353 | sonatype-nexus-staging 354 | ${maven.deploy.skip} 355 | 356 | 357 | 358 |
359 | 360 | 361 | 362 | deploy-sonatype 363 | 364 | 365 | sonatype-nexus-staging 366 | http://oss.sonatype.org/service/local/staging/deploy/maven2 367 | 368 | 369 | 370 | 371 | 372 | sign-artifacts 373 | 374 | 375 | performRelease 376 | true 377 | 378 | 379 | 380 | 381 | 382 | maven-gpg-plugin 383 | 384 | 385 | sign-artifacts 386 | verify 387 | 388 | sign 389 | 390 | 391 | Terracotta Release Engineer 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | https://github.com/ehcache/sizeof 403 | scm:git:https://github.com/ehcache/sizeof.git 404 | scm:git:git@github.com:ehcache/sizeof.git 405 | 406 | 407 |
408 | -------------------------------------------------------------------------------- /src/hidden/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Agent-Class: org.ehcache.sizeof.impl.SizeOfAgent 3 | Premain-Class: org.ehcache.sizeof.impl.SizeOfAgent 4 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/Configuration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.SizeOfFilter; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Arrays; 23 | import java.util.Collections; 24 | import java.util.List; 25 | 26 | /** 27 | * @author Alex Snaps 28 | */ 29 | public final class Configuration { 30 | private final int maxDepth; 31 | private final boolean abort; 32 | private final boolean silent; 33 | private final SizeOfFilter[] filters; 34 | 35 | public Configuration(final int maxDepth, final boolean abort, final boolean silent, final SizeOfFilter... filters) { 36 | this.maxDepth = maxDepth; 37 | this.abort = abort; 38 | this.silent = silent; 39 | this.filters = filters; 40 | } 41 | 42 | public int getMaxDepth() { 43 | return maxDepth; 44 | } 45 | 46 | public boolean isAbort() { 47 | return abort; 48 | } 49 | 50 | public boolean isSilent() { 51 | return silent; 52 | } 53 | 54 | public SizeOfFilter[] getFilters() { 55 | return filters; 56 | } 57 | 58 | public static final class Builder { 59 | 60 | private int maxDepth; 61 | private boolean silent; 62 | private boolean abort; 63 | private final List filters = new ArrayList<>(); 64 | 65 | public Builder() { 66 | } 67 | 68 | public Builder(Configuration cfg) { 69 | maxDepth(cfg.maxDepth); 70 | silent(cfg.silent); 71 | abort(cfg.abort); 72 | Collections.addAll(filters, cfg.filters); 73 | } 74 | 75 | public Builder maxDepth(int maxDepth) { 76 | this.maxDepth = maxDepth; 77 | return this; 78 | } 79 | 80 | public Builder silent(boolean silent) { 81 | this.silent = silent; 82 | return this; 83 | } 84 | 85 | public Builder abort(boolean abort) { 86 | this.abort = abort; 87 | return this; 88 | } 89 | 90 | public Builder addFilter(SizeOfFilter filter) { 91 | if (!filters.contains(filter)) { 92 | filters.add(filter); 93 | } 94 | return this; 95 | } 96 | 97 | public Builder addFilters(SizeOfFilter... filters) { 98 | for (SizeOfFilter filter : filters) { 99 | addFilter(filter); 100 | } 101 | return this; 102 | } 103 | 104 | public Builder removeFilter(SizeOfFilter filter) { 105 | filters.remove(filter); 106 | return this; 107 | } 108 | 109 | public Builder removeFilters(SizeOfFilter... filters) { 110 | this.filters.removeAll(Arrays.asList(filters)); 111 | return this; 112 | } 113 | 114 | public Builder clearlFilters() { 115 | this.filters.clear(); 116 | return this; 117 | } 118 | 119 | public Configuration build() { 120 | return new Configuration(maxDepth, abort, silent, filters.toArray(new SizeOfFilter[filters.size()])); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/Filter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import java.lang.reflect.Field; 20 | 21 | /** 22 | * Filters all the sizing operation performed by a SizeOfEngine instance 23 | * 24 | * @author Alex Snaps 25 | */ 26 | public interface Filter { 27 | 28 | /** 29 | * Adds the class to the ignore list. Can be strict, or include subtypes 30 | * 31 | * @param clazz the class to ignore 32 | * @param strict true if to be ignored strictly, or false to include sub-classes 33 | */ 34 | void ignoreInstancesOf(final Class clazz, final boolean strict); 35 | 36 | /** 37 | * Adds a field to the ignore list. When that field is walked to by the SizeOfEngine, it won't navigate the graph further 38 | * 39 | * @param field the field to stop navigating the graph at 40 | */ 41 | void ignoreField(final Field field); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/FilterConfigurator.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | /** 20 | * A configurator, instantiated by the {@link java.util.ServiceLoader}, that will get to configure the {@link Filter} 21 | * 22 | * @author Alex Snaps 23 | */ 24 | public interface FilterConfigurator { 25 | 26 | /** 27 | * Invoked by the framework to let this instance configure the filter 28 | * @param filter the filter managed by the framework 29 | */ 30 | void configure(Filter filter); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/FlyWeightType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import java.lang.reflect.Field; 20 | import java.lang.reflect.Modifier; 21 | import java.math.BigDecimal; 22 | import java.math.BigInteger; 23 | import java.math.MathContext; 24 | import java.net.Proxy; 25 | import java.nio.charset.CodingErrorAction; 26 | import java.util.Collections; 27 | import java.util.HashMap; 28 | import java.util.IdentityHashMap; 29 | import java.util.Locale; 30 | import java.util.Map; 31 | import java.util.Set; 32 | import java.util.logging.Logger; 33 | 34 | import javax.xml.datatype.DatatypeConstants; 35 | import javax.xml.namespace.QName; 36 | 37 | /** 38 | * Enum with all the flyweight types that we check for sizeOf measurements 39 | * 40 | * @author Alex Snaps 41 | */ 42 | @SuppressWarnings("BoxingBoxedValue") 43 | // Don't be smart IDE, these _ARE_ required, we want access to the instances in the cache. 44 | enum FlyweightType { 45 | 46 | /** 47 | * java.lang.Enum 48 | */ 49 | ENUM(Enum.class) { 50 | @Override 51 | boolean isShared(final Object obj) { return true; } 52 | }, 53 | /** 54 | * java.lang.Class 55 | */ 56 | CLASS(Class.class) { 57 | @Override 58 | boolean isShared(final Object obj) { return true; } 59 | }, 60 | // XXX There is no nullipotent way of determining the interned status of a string 61 | // There are numerous String constants within the JDK (see list at http://docs.oracle.com/javase/7/docs/api/constant-values.html), 62 | // but enumerating all of them would lead to lots of == tests. 63 | //STRING(String.class) { 64 | // @Override 65 | // boolean isShared(final Object obj) { return obj == ((String)obj).intern(); } 66 | //}, 67 | /** 68 | * java.lang.Boolean 69 | */ 70 | BOOLEAN(Boolean.class) { 71 | @Override 72 | boolean isShared(final Object obj) { return obj == Boolean.TRUE || obj == Boolean.FALSE; } 73 | }, 74 | /** 75 | * java.lang.Integer 76 | */ 77 | INTEGER(Integer.class) { 78 | @Override 79 | boolean isShared(final Object obj) { 80 | int value = (Integer)obj; 81 | return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE && obj == Integer.valueOf(value); 82 | } 83 | }, 84 | /** 85 | * java.lang.Short 86 | */ 87 | SHORT(Short.class) { 88 | @Override 89 | boolean isShared(final Object obj) { 90 | short value = (Short)obj; 91 | return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE && obj == Short.valueOf(value); 92 | } 93 | }, 94 | /** 95 | * java.lang.Byte 96 | */ 97 | BYTE(Byte.class) { 98 | @Override 99 | boolean isShared(final Object obj) { return obj == Byte.valueOf((Byte)obj); } 100 | }, 101 | /** 102 | * java.lang.Long 103 | */ 104 | LONG(Long.class) { 105 | @Override 106 | boolean isShared(final Object obj) { 107 | long value = (Long)obj; 108 | return value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE && obj == Long.valueOf(value); 109 | } 110 | }, 111 | /** 112 | * java.math.BigInteger 113 | */ 114 | BIGINTEGER(BigInteger.class) { 115 | @Override 116 | boolean isShared(final Object obj) { 117 | return obj == BigInteger.ZERO || obj == BigInteger.ONE || obj == BigInteger.TEN; 118 | } 119 | }, 120 | /** 121 | * java.math.BigDecimal 122 | */ 123 | BIGDECIMAL(BigDecimal.class) { 124 | @Override 125 | boolean isShared(final Object obj) { 126 | return obj == BigDecimal.ZERO || obj == BigDecimal.ONE || obj == BigDecimal.TEN; 127 | } 128 | }, 129 | /** 130 | * java.math.MathContext 131 | */ 132 | MATHCONTEXT(MathContext.class) { 133 | @Override 134 | boolean isShared(final Object obj) { 135 | return obj == MathContext.UNLIMITED || obj == MathContext.DECIMAL32 || obj == MathContext.DECIMAL64 || obj == MathContext.DECIMAL128; 136 | } 137 | }, 138 | /** 139 | * java.lang.Character 140 | */ 141 | CHARACTER(Character.class) { 142 | @Override 143 | boolean isShared(final Object obj) { return (Character)obj <= Byte.MAX_VALUE && obj == Character.valueOf((Character)obj); } 144 | }, 145 | /** 146 | * java.lang.Locale 147 | */ 148 | LOCALE(Locale.class) { 149 | @Override 150 | boolean isShared(final Object obj) { 151 | return obj instanceof Locale && GLOBAL_LOCALES.contains(obj); 152 | } 153 | }, 154 | /** 155 | * java.util.Logger 156 | */ 157 | LOGGER(Logger.class) { 158 | @Override 159 | @SuppressWarnings("deprecation") 160 | boolean isShared(final Object obj) { return obj == Logger.global; } 161 | }, 162 | /** 163 | * java.net.Proxy 164 | */ 165 | PROXY(Proxy.class) { 166 | @Override 167 | boolean isShared(final Object obj) { return obj == Proxy.NO_PROXY; } 168 | }, 169 | /** 170 | * java.nio.charset.CodingErrorAction 171 | */ 172 | CODINGERRORACTION(CodingErrorAction.class) { 173 | @Override 174 | boolean isShared(final Object obj) { return true; } 175 | }, 176 | /** 177 | * javax.xml.datatype.DatatypeConstants.Field 178 | */ 179 | DATATYPECONSTANTS_FIELD(DatatypeConstants.Field.class) { 180 | @Override 181 | boolean isShared(final Object obj) { return true; } 182 | }, 183 | /** 184 | * javax.xml.namespace.QName 185 | */ 186 | QNAME(QName.class) { 187 | @Override 188 | boolean isShared(final Object obj) { 189 | return obj == DatatypeConstants.DATETIME 190 | || obj == DatatypeConstants.TIME 191 | || obj == DatatypeConstants.DATE 192 | || obj == DatatypeConstants.GYEARMONTH 193 | || obj == DatatypeConstants.GMONTHDAY 194 | || obj == DatatypeConstants.GYEAR 195 | || obj == DatatypeConstants.GMONTH 196 | || obj == DatatypeConstants.GDAY 197 | || obj == DatatypeConstants.DURATION 198 | || obj == DatatypeConstants.DURATION_DAYTIME 199 | || obj == DatatypeConstants.DURATION_YEARMONTH; 200 | } 201 | }, 202 | /** 203 | * misc comparisons that can not rely on the object's class. 204 | */ 205 | MISC(Void.class) { 206 | @Override 207 | boolean isShared(final Object obj) { 208 | boolean emptyCollection = obj == Collections.EMPTY_SET || obj == Collections.EMPTY_LIST || obj == Collections.EMPTY_MAP; 209 | boolean systemStream = obj == System.in || obj == System.out || obj == System.err; 210 | return emptyCollection || systemStream || obj == String.CASE_INSENSITIVE_ORDER; 211 | } 212 | }; 213 | 214 | private static final Map, FlyweightType> TYPE_MAPPINGS = new HashMap<>(); 215 | 216 | static { 217 | for (FlyweightType type : FlyweightType.values()) { 218 | TYPE_MAPPINGS.put(type.clazz, type); 219 | } 220 | } 221 | 222 | private static final Set GLOBAL_LOCALES; 223 | 224 | static { 225 | Map locales = new IdentityHashMap<>(); 226 | for (Field f : Locale.class.getFields()) { 227 | int modifiers = f.getModifiers(); 228 | if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Locale.class.equals(f.getType())) { 229 | try { 230 | locales.put((Locale)f.get(null), null); 231 | } catch (IllegalArgumentException | IllegalAccessException e) { 232 | // 233 | } 234 | } 235 | } 236 | GLOBAL_LOCALES = locales.keySet(); 237 | } 238 | 239 | private final Class clazz; 240 | 241 | FlyweightType(final Class clazz) { 242 | this.clazz = clazz; 243 | } 244 | 245 | /** 246 | * Whether this is a shared object 247 | * 248 | * @param obj the object to check for 249 | * @return true, if shared 250 | */ 251 | abstract boolean isShared(Object obj); 252 | 253 | /** 254 | * Will return the Flyweight enum instance for the flyweight Class, or null if type isn't flyweight 255 | * 256 | * @param aClazz the class we need the FlyweightType instance for 257 | * @return the FlyweightType, or null 258 | */ 259 | static FlyweightType getFlyweightType(final Class aClazz) { 260 | if (aClazz.isEnum() || (aClazz.getSuperclass() != null && aClazz.getSuperclass().isEnum())) { 261 | return ENUM; 262 | } else { 263 | FlyweightType flyweightType = TYPE_MAPPINGS.get(aClazz); 264 | return flyweightType != null ? flyweightType : MISC; 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/ObjectGraphWalker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.SizeOfFilter; 20 | import org.ehcache.sizeof.util.WeakIdentityConcurrentMap; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import java.lang.ref.SoftReference; 25 | import java.lang.reflect.Array; 26 | import java.lang.reflect.Field; 27 | import java.lang.reflect.Modifier; 28 | import java.util.ArrayDeque; 29 | import java.util.ArrayList; 30 | import java.util.Collection; 31 | import java.util.Deque; 32 | import java.util.IdentityHashMap; 33 | import java.util.Set; 34 | 35 | import static java.util.Collections.newSetFromMap; 36 | 37 | /** 38 | * This will walk an object graph and let you execute some "function" along the way 39 | * 40 | * @author Alex Snaps 41 | */ 42 | final class ObjectGraphWalker { 43 | 44 | private static final Logger LOG = LoggerFactory.getLogger(ObjectGraphWalker.class); 45 | private static final String VERBOSE_DEBUG_LOGGING = "org.ehcache.sizeof.verboseDebugLogging"; 46 | private static final boolean USE_VERBOSE_DEBUG_LOGGING; 47 | 48 | private final WeakIdentityConcurrentMap, SoftReference>> fieldCache = 49 | new WeakIdentityConcurrentMap<>(); 50 | private final WeakIdentityConcurrentMap, Boolean> classCache = 51 | new WeakIdentityConcurrentMap<>(); 52 | 53 | private final boolean bypassFlyweight; 54 | private final SizeOfFilter sizeOfFilter; 55 | 56 | private final Visitor visitor; 57 | 58 | static { 59 | USE_VERBOSE_DEBUG_LOGGING = getVerboseSizeOfDebugLogging(); 60 | } 61 | 62 | /** 63 | * Constructor 64 | * 65 | * @param visitor the visitor to use 66 | * @param filter the filtering 67 | * @param bypassFlyweight the filtering 68 | * @see Visitor 69 | * @see SizeOfFilter 70 | */ 71 | ObjectGraphWalker(Visitor visitor, SizeOfFilter filter, final boolean bypassFlyweight) { 72 | if(visitor == null) { 73 | throw new NullPointerException("Visitor can't be null"); 74 | } 75 | if(filter == null) { 76 | throw new NullPointerException("SizeOfFilter can't be null"); 77 | } 78 | this.visitor = visitor; 79 | this.sizeOfFilter = filter; 80 | this.bypassFlyweight = bypassFlyweight; 81 | } 82 | 83 | private static boolean getVerboseSizeOfDebugLogging() { 84 | 85 | String verboseString = System.getProperty(VERBOSE_DEBUG_LOGGING, "false").toLowerCase(); 86 | 87 | return verboseString.equals("true"); 88 | } 89 | 90 | /** 91 | * The visitor to execute the function on each node of the graph 92 | * This is only to be used for the sizing of an object graph in memory! 93 | */ 94 | interface Visitor { 95 | /** 96 | * The visit method executed on each node 97 | * 98 | * @param object the reference at that node 99 | * @return a long for you to do things with... 100 | */ 101 | long visit(Object object); 102 | } 103 | 104 | /** 105 | * Walk the graph and call into the "visitor" 106 | * 107 | * @param root the roots of the objects (a shared graph will only be visited once) 108 | * @return the sum of all Visitor#visit returned values 109 | */ 110 | long walk(Object... root) { 111 | return walk(null, root); 112 | } 113 | 114 | /** 115 | * Walk the graph and call into the "visitor" 116 | * 117 | * @param visitorListener A decorator for the Visitor 118 | * @param root the roots of the objects (a shared graph will only be visited once) 119 | * @return the sum of all Visitor#visit returned values 120 | */ 121 | long walk(VisitorListener visitorListener, Object... root) { 122 | final StringBuilder traversalDebugMessage; 123 | if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) { 124 | traversalDebugMessage = new StringBuilder(); 125 | } else { 126 | traversalDebugMessage = null; 127 | } 128 | long result = 0; 129 | Deque toVisit = new ArrayDeque<>(); 130 | Set visited = newSetFromMap(new IdentityHashMap<>()); 131 | 132 | if (root != null) { 133 | if (traversalDebugMessage != null) { 134 | traversalDebugMessage.append("visiting "); 135 | } 136 | for (Object object : root) { 137 | nullSafeAdd(toVisit, object); 138 | if (traversalDebugMessage != null && object != null) { 139 | traversalDebugMessage.append(object.getClass().getName()) 140 | .append("@").append(System.identityHashCode(object)).append(", "); 141 | } 142 | } 143 | if (traversalDebugMessage != null) { 144 | traversalDebugMessage.deleteCharAt(traversalDebugMessage.length() - 2).append("\n"); 145 | } 146 | } 147 | 148 | while (!toVisit.isEmpty()) { 149 | 150 | Object ref = toVisit.pop(); 151 | 152 | if (visited.add(ref)) { 153 | Class refClass = ref.getClass(); 154 | if (!byPassIfFlyweight(ref) && shouldWalkClass(refClass)) { 155 | if (refClass.isArray() && !refClass.getComponentType().isPrimitive()) { 156 | for (int i = 0; i < Array.getLength(ref); i++) { 157 | nullSafeAdd(toVisit, Array.get(ref, i)); 158 | } 159 | } else { 160 | for (Field field : getFilteredFields(refClass)) { 161 | try { 162 | nullSafeAdd(toVisit, field.get(ref)); 163 | } catch (IllegalAccessException ex) { 164 | throw new RuntimeException(ex); 165 | } 166 | } 167 | } 168 | 169 | final long visitSize = visitor.visit(ref); 170 | if (visitorListener != null) { 171 | visitorListener.visited(ref, visitSize); 172 | } 173 | if (traversalDebugMessage != null) { 174 | traversalDebugMessage.append(" ").append(visitSize).append("b\t\t") 175 | .append(ref.getClass().getName()).append("@").append(System.identityHashCode(ref)).append("\n"); 176 | } 177 | result += visitSize; 178 | } else if (traversalDebugMessage != null) { 179 | traversalDebugMessage.append(" ignored\t") 180 | .append(ref.getClass().getName()).append("@").append(System.identityHashCode(ref)).append("\n"); 181 | } 182 | } 183 | } 184 | 185 | if (traversalDebugMessage != null) { 186 | traversalDebugMessage.append("Total size: ").append(result).append(" bytes\n"); 187 | LOG.debug(traversalDebugMessage.toString()); 188 | } 189 | return result; 190 | } 191 | 192 | /** 193 | * Returns the filtered fields for a particular type 194 | * 195 | * @param refClass the type 196 | * @return A collection of fields to be visited 197 | */ 198 | private Collection getFilteredFields(Class refClass) { 199 | SoftReference> ref = fieldCache.get(refClass); 200 | Collection fieldList = ref != null ? ref.get() : null; 201 | if (fieldList != null) { 202 | return fieldList; 203 | } else { 204 | Collection result; 205 | result = sizeOfFilter.filterFields(refClass, getAllFields(refClass)); 206 | if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) { 207 | for (Field field : result) { 208 | if (Modifier.isTransient(field.getModifiers())) { 209 | LOG.debug("SizeOf engine walking transient field '{}' of class {}", field.getName(), refClass.getName()); 210 | } 211 | } 212 | } 213 | fieldCache.put(refClass, new SoftReference<>(result)); 214 | return result; 215 | } 216 | } 217 | 218 | private boolean shouldWalkClass(Class refClass) { 219 | Boolean cached = classCache.get(refClass); 220 | if (cached == null) { 221 | cached = sizeOfFilter.filterClass(refClass); 222 | classCache.put(refClass, cached); 223 | } 224 | return cached; 225 | } 226 | 227 | private static void nullSafeAdd(final Deque toVisit, final Object o) { 228 | if (o != null) { 229 | toVisit.push(o); 230 | } 231 | } 232 | 233 | /** 234 | * Returns all non-primitive fields for the entire class hierarchy of a type 235 | * 236 | * @param refClass the type 237 | * @return all fields for that type 238 | */ 239 | private static Collection getAllFields(Class refClass) { 240 | Collection fields = new ArrayList<>(); 241 | for (Class klazz = refClass; klazz != null; klazz = klazz.getSuperclass()) { 242 | for (Field field : klazz.getDeclaredFields()) { 243 | if (!Modifier.isStatic(field.getModifiers()) && 244 | !field.getType().isPrimitive()) { 245 | try { 246 | field.setAccessible(true); 247 | } catch (SecurityException e) { 248 | LOG.error("Security settings prevent Ehcache from accessing the subgraph beneath '{}'" + 249 | " - cache sizes may be underestimated as a result", field, e); 250 | continue; 251 | } catch (RuntimeException e) { 252 | LOG.warn("The JVM is preventing Ehcache from accessing the subgraph beneath '{}'" + 253 | " - cache sizes may be underestimated as a result", field, e); 254 | continue; 255 | } 256 | fields.add(field); 257 | } 258 | } 259 | } 260 | return fields; 261 | } 262 | 263 | private boolean byPassIfFlyweight(Object obj) { 264 | if(bypassFlyweight) { 265 | FlyweightType type = FlyweightType.getFlyweightType(obj.getClass()); 266 | return type != null && type.isShared(obj); 267 | } 268 | return false; 269 | } 270 | 271 | } 272 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/SizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.CombinationSizeOfFilter; 20 | import org.ehcache.sizeof.filters.SizeOfFilter; 21 | import org.ehcache.sizeof.impl.AgentSizeOf; 22 | import org.ehcache.sizeof.impl.ReflectionSizeOf; 23 | import org.ehcache.sizeof.impl.UnsafeSizeOf; 24 | import org.ehcache.sizeof.util.WeakIdentityConcurrentMap; 25 | 26 | /** 27 | * Abstract sizeOf for Java. It will rely on a proper sizeOf to measure sizes of entire object graphs 28 | * 29 | * @author Alex Snaps 30 | */ 31 | public abstract class SizeOf { 32 | 33 | private final ObjectGraphWalker walker; 34 | 35 | /** 36 | * Builds a new SizeOf that will filter fields according to the provided filter 37 | * 38 | * @param fieldFilter The filter to apply 39 | * @param caching whether to cache reflected fields 40 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored 41 | * @see org.ehcache.sizeof.filters.SizeOfFilter 42 | */ 43 | public SizeOf(SizeOfFilter fieldFilter, boolean caching, boolean bypassFlyweight) { 44 | ObjectGraphWalker.Visitor visitor; 45 | if (caching) { 46 | visitor = new CachingSizeOfVisitor(); 47 | } else { 48 | visitor = new SizeOfVisitor(); 49 | } 50 | this.walker = new ObjectGraphWalker(visitor, fieldFilter, bypassFlyweight); 51 | } 52 | 53 | /** 54 | * Calculates the size in memory (heap) of the instance passed in, not navigating the down graph 55 | * 56 | * @param obj the object to measure the size of 57 | * @return the object size in memory in bytes 58 | */ 59 | public abstract long sizeOf(Object obj); 60 | 61 | /** 62 | * Measures the size in memory (heap) of the objects passed in, walking their graph down 63 | * Any overlap of the graphs being passed in will be recognized and only measured once 64 | * 65 | * @param listener A listener to visited objects 66 | * @param obj the root objects of the graphs to measure 67 | * @return the total size in bytes for these objects 68 | * @see #sizeOf(Object) 69 | */ 70 | public long deepSizeOf(VisitorListener listener, Object... obj) { 71 | return walker.walk(listener, obj); 72 | } 73 | 74 | public long deepSizeOf(Object... obj) { 75 | return walker.walk(null, obj); 76 | } 77 | 78 | public static SizeOf newInstance(final SizeOfFilter... filters) { 79 | return newInstance(true, true, filters); 80 | } 81 | 82 | public static SizeOf newInstance(boolean bypassFlyweight, boolean cache, final SizeOfFilter... filters) { 83 | final SizeOfFilter filter = new CombinationSizeOfFilter(filters); 84 | try { 85 | return new AgentSizeOf(filter, cache, bypassFlyweight); 86 | } catch (UnsupportedOperationException e) { 87 | try { 88 | return new UnsafeSizeOf(filter, cache, bypassFlyweight); 89 | } catch (UnsupportedOperationException f) { 90 | try { 91 | return new ReflectionSizeOf(filter, cache, bypassFlyweight); 92 | } catch (UnsupportedOperationException g) { 93 | throw new UnsupportedOperationException("A suitable SizeOf engine could not be loaded: " + e + ", " + f + ", " + g); 94 | } 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * Will return the sizeOf each instance 101 | */ 102 | private class SizeOfVisitor implements ObjectGraphWalker.Visitor { 103 | 104 | /** 105 | * {@inheritDoc} 106 | */ 107 | public long visit(Object object) { 108 | return sizeOf(object); 109 | } 110 | } 111 | 112 | /** 113 | * Will Cache already visited types 114 | */ 115 | private class CachingSizeOfVisitor implements ObjectGraphWalker.Visitor { 116 | private final WeakIdentityConcurrentMap, Long> cache = new WeakIdentityConcurrentMap<>(); 117 | 118 | /** 119 | * {@inheritDoc} 120 | */ 121 | public long visit(final Object object) { 122 | Class klazz = object.getClass(); 123 | Long cachedSize = cache.get(klazz); 124 | if (cachedSize == null) { 125 | if (klazz.isArray()) { 126 | return sizeOf(object); 127 | } else { 128 | long size = sizeOf(object); 129 | cache.put(klazz, size); 130 | return size; 131 | } 132 | } else { 133 | return cachedSize; 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/SizeOfFilterSource.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter; 20 | import org.ehcache.sizeof.filters.SizeOfFilter; 21 | import org.ehcache.sizeof.filters.TypeFilter; 22 | 23 | import java.lang.reflect.Field; 24 | import java.lang.reflect.Modifier; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.ServiceLoader; 28 | import java.util.concurrent.CopyOnWriteArrayList; 29 | 30 | /** 31 | * @author Alex Snaps 32 | */ 33 | public final class SizeOfFilterSource implements Filter { 34 | 35 | private final CopyOnWriteArrayList filters = new CopyOnWriteArrayList<>(); 36 | private final TypeFilter typeFilter = new TypeFilter(); 37 | 38 | public SizeOfFilterSource(boolean registerAnnotationFilter) { 39 | filters.add(typeFilter); 40 | if (registerAnnotationFilter) { 41 | filters.add(new AnnotationSizeOfFilter()); 42 | } 43 | applyMutators(); 44 | } 45 | 46 | private void applyMutators() { 47 | applyMutators(SizeOfFilterSource.class.getClassLoader()); 48 | } 49 | 50 | void applyMutators(final ClassLoader classLoader) { 51 | final ServiceLoader loader = ServiceLoader.load(FilterConfigurator.class, classLoader); 52 | for (FilterConfigurator filterConfigurator : loader) { 53 | filterConfigurator.configure(this); 54 | } 55 | } 56 | 57 | public SizeOfFilter[] getFilters() { 58 | List allFilters = new ArrayList<>(filters); 59 | return allFilters.toArray(new SizeOfFilter[allFilters.size()]); 60 | } 61 | 62 | public void ignoreInstancesOf(final Class clazz, final boolean strict) { 63 | typeFilter.addClass(clazz, Modifier.isFinal(clazz.getModifiers()) || strict); 64 | } 65 | 66 | public void ignoreField(final Field field) { 67 | typeFilter.addField(field); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/VisitorListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | /** 20 | * @author Alex Snaps 21 | */ 22 | public interface VisitorListener { 23 | 24 | void visited(final Object object, final long size); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/annotations/AnnotationProxyFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.annotations; 18 | 19 | import java.lang.annotation.Annotation; 20 | import java.lang.reflect.InvocationHandler; 21 | import java.lang.reflect.Method; 22 | import java.lang.reflect.Proxy; 23 | 24 | /** 25 | * Allows you to transform the type of your custom annotation to a reference annotation type. 26 | * It can come handy when you want to allow the consumers of your library not to depend on your API because of the annotations, still allowing them to use the original annotation methods. 27 | *

28 | * Example : 29 | *

30 | * //getting a custom annotation from a class 31 | * my.Annotation customAnnotation = klazz.getAnnotation(my.Annotation.class); 32 | * //if this annotation is "similar" (duck-typing, same methods) to the reference one, I can get a proxy to it, whose type is the reference annotation 33 | * ehcache.Annotation annotation = AnnotationProxyFactory.getAnnotationProxy(customAnnotation, ehcache.Annotation.class); 34 | *

35 | * //so my library can apply the behavior when the default annotation is used 36 | * 37 | * @author Anthony Dahanne 38 | * @ehcache.Annotation(action="true") public class UserClass {} 39 | *

40 | * //or when a custom one is used, since all calls to action() will be caught and redirected to the custom annotation action method, if it exists, 41 | * //or fall back to the reference action method 42 | * @my.Annotation(action="true") public class UserClass {} 43 | */ 44 | public final class AnnotationProxyFactory { 45 | 46 | 47 | private AnnotationProxyFactory() { 48 | //not to instantiate 49 | } 50 | 51 | /** 52 | * Returns a proxy on the customAnnotation, having the same type than the referenceAnnotation 53 | * 54 | * @param customAnnotation annotation proxied 55 | * @param referenceAnnotation type of the returned annotation 56 | * @return proxied customAnnotation with the type of referenceAnnotation 57 | */ 58 | @SuppressWarnings("unchecked") 59 | public static T getAnnotationProxy(Annotation customAnnotation, Class referenceAnnotation) { 60 | InvocationHandler handler = new AnnotationInvocationHandler(customAnnotation); 61 | return (T)Proxy.newProxyInstance(referenceAnnotation.getClassLoader(), new Class[] { referenceAnnotation }, handler); 62 | } 63 | 64 | /** 65 | * Invocation handler implementing an invoke method that redirects every method call to the custom annotation method 66 | * when possible; if not returns the reference annotation method default value 67 | */ 68 | private static class AnnotationInvocationHandler implements InvocationHandler { 69 | 70 | private final Annotation customAnnotation; 71 | 72 | public AnnotationInvocationHandler(Annotation customAnnotation) { 73 | this.customAnnotation = customAnnotation; 74 | } 75 | 76 | public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { 77 | //trying to call the method on the custom annotation, if it exists 78 | Method methodOnCustom = getMatchingMethodOnGivenAnnotation(method); 79 | if (methodOnCustom != null) { 80 | return methodOnCustom.invoke(customAnnotation, args); 81 | } else { 82 | //otherwise getting the default value of the reference annotation method 83 | Object defaultValue = method.getDefaultValue(); 84 | if (defaultValue != null) { 85 | return defaultValue; 86 | } 87 | throw new UnsupportedOperationException( 88 | "The method \"" 89 | + method.getName() 90 | + "\" does not exist in the custom annotation, and there is no default value for" 91 | + " it in the reference annotation, please implement this method in your custom annotation."); 92 | } 93 | } 94 | 95 | private Method getMatchingMethodOnGivenAnnotation(Method method) { 96 | try { 97 | Method customMethod = customAnnotation.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes()); 98 | if (customMethod.getReturnType().isAssignableFrom(method.getReturnType())) { 99 | return customMethod; 100 | } 101 | return null; 102 | } catch (NoSuchMethodException e) { 103 | return null; 104 | } 105 | } 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/annotations/IgnoreSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.annotations; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Annotation to ignore a field, type or entire package while doing a SizeOf measurement 26 | * 27 | * @author Chris Dennis 28 | * @see org.ehcache.sizeof.SizeOf 29 | */ 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE }) 32 | public @interface IgnoreSizeOf { 33 | 34 | /** 35 | * Controls whether the annotation, when applied to a {@link ElementType#TYPE type} is to be applied to all its subclasses 36 | * as well or solely on that type only. true if inherited by subtypes, false otherwise 37 | */ 38 | boolean inherited() default false; 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/filters/AnnotationSizeOfFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.filters; 18 | 19 | import org.ehcache.sizeof.annotations.AnnotationProxyFactory; 20 | import org.ehcache.sizeof.annotations.IgnoreSizeOf; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import java.lang.annotation.Annotation; 25 | import java.lang.reflect.AnnotatedElement; 26 | import java.lang.reflect.Field; 27 | import java.util.Collection; 28 | import java.util.Iterator; 29 | import java.util.regex.Matcher; 30 | import java.util.regex.Pattern; 31 | import java.util.regex.PatternSyntaxException; 32 | 33 | public final class AnnotationSizeOfFilter implements SizeOfFilter { 34 | 35 | private static final String IGNORE_SIZE_OF_VM_ARGUMENT = AnnotationSizeOfFilter.class.getName() + ".pattern"; 36 | 37 | private static final Logger LOG = LoggerFactory.getLogger(AnnotationSizeOfFilter.class.getName()); 38 | 39 | //default is *cache.*IgnoreSizeOf 40 | private static final String IGNORE_SIZE_OF_DEFAULT_REGEXP = "^.*cache\\..*IgnoreSizeOf$"; 41 | private static final Pattern IGNORE_SIZE_OF_PATTERN; 42 | 43 | static { 44 | String ignoreSizeOfRegexpVMArg = System.getProperty(IGNORE_SIZE_OF_VM_ARGUMENT); 45 | String ignoreSizeOfRegexp = ignoreSizeOfRegexpVMArg != null ? ignoreSizeOfRegexpVMArg : IGNORE_SIZE_OF_DEFAULT_REGEXP; 46 | Pattern localPattern; 47 | try { 48 | localPattern = Pattern.compile(ignoreSizeOfRegexp); 49 | LOG.info("Using regular expression provided through VM argument " 50 | + IGNORE_SIZE_OF_VM_ARGUMENT 51 | + " for IgnoreSizeOf annotation : " 52 | + ignoreSizeOfRegexp); 53 | } catch (PatternSyntaxException e) { 54 | throw new IllegalArgumentException("Invalid regular expression provided through VM argument " 55 | + IGNORE_SIZE_OF_VM_ARGUMENT 56 | + " : \n" 57 | + e.getMessage() 58 | + "\n using default regular expression for IgnoreSizeOf annotation : " 59 | + IGNORE_SIZE_OF_DEFAULT_REGEXP); 60 | } 61 | IGNORE_SIZE_OF_PATTERN = localPattern; 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | public Collection filterFields(Class klazz, Collection fields) { 68 | for (Iterator it = fields.iterator(); it.hasNext(); ) { 69 | 70 | Field field = it.next(); 71 | IgnoreSizeOf annotationOnField = getAnnotationOn(field, IgnoreSizeOf.class, IGNORE_SIZE_OF_PATTERN); 72 | if (annotationOnField != null) { 73 | it.remove(); 74 | } 75 | } 76 | return fields; 77 | } 78 | 79 | /** 80 | * {@inheritDoc} 81 | */ 82 | public boolean filterClass(Class klazz) { 83 | boolean classAnnotated = isAnnotationPresentOrInherited(klazz); 84 | Package pack = klazz.getPackage(); 85 | IgnoreSizeOf annotationOnPackage = pack == null ? null : getAnnotationOn(pack, IgnoreSizeOf.class, IGNORE_SIZE_OF_PATTERN); 86 | boolean packageAnnotated = annotationOnPackage != null; 87 | return !classAnnotated && !packageAnnotated; 88 | } 89 | 90 | private boolean isAnnotationPresentOrInherited(final Class instanceKlazz) { 91 | Class klazz = instanceKlazz; 92 | while (klazz != null) { 93 | IgnoreSizeOf annotationOnClass = getAnnotationOn(klazz, IgnoreSizeOf.class, IGNORE_SIZE_OF_PATTERN); 94 | if (annotationOnClass != null && (klazz == instanceKlazz || annotationOnClass.inherited())) { 95 | return true; 96 | } 97 | klazz = klazz.getSuperclass(); 98 | } 99 | return false; 100 | } 101 | 102 | private boolean validateCustomAnnotationPattern(String canonicalName, Pattern matchingAnnotationPattern) { 103 | Matcher matcher = matchingAnnotationPattern.matcher(canonicalName); 104 | 105 | boolean found = matcher.matches(); 106 | if (found) { 107 | LOG.debug(canonicalName + " matched IgnoreSizeOf annotation pattern " + IGNORE_SIZE_OF_PATTERN); 108 | } 109 | return found; 110 | } 111 | 112 | //EHC-938 : looking for all types of IgnoreSizeOf annotations 113 | private T getAnnotationOn(AnnotatedElement element, Class referenceAnnotation, Pattern matchingAnnotationPattern) { 114 | T matchingAnnotation = null; 115 | Annotation[] annotations = element.getAnnotations(); 116 | for (Annotation annotation : annotations) { 117 | if (validateCustomAnnotationPattern(annotation.annotationType().getName(), matchingAnnotationPattern)) { 118 | if (matchingAnnotation != null) { 119 | throw new IllegalStateException("You are not allowed to use more than one @" + referenceAnnotation.getName() 120 | + " annotations for the same element : " 121 | + element.toString()); 122 | } 123 | matchingAnnotation = AnnotationProxyFactory.getAnnotationProxy(annotation, referenceAnnotation); 124 | } 125 | } 126 | return matchingAnnotation; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/filters/CombinationSizeOfFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.filters; 18 | 19 | import java.lang.reflect.Field; 20 | import java.util.Collection; 21 | 22 | /** 23 | * Filter combining multiple filters 24 | * 25 | * @author Chris Dennis 26 | */ 27 | public class CombinationSizeOfFilter implements SizeOfFilter { 28 | 29 | private final SizeOfFilter[] filters; 30 | 31 | /** 32 | * Constructs a filter combining multiple ones 33 | * 34 | * @param filters the filters to combine 35 | */ 36 | public CombinationSizeOfFilter(SizeOfFilter... filters) { 37 | this.filters = filters; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | public Collection filterFields(Class klazz, Collection fields) { 44 | Collection current = fields; 45 | for (SizeOfFilter filter : filters) { 46 | current = filter.filterFields(klazz, current); 47 | } 48 | return current; 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public boolean filterClass(Class klazz) { 55 | for (SizeOfFilter filter : filters) { 56 | if (!filter.filterClass(klazz)) { 57 | return false; 58 | } 59 | } 60 | return true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/filters/SizeOfFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.filters; 18 | 19 | import java.lang.reflect.Field; 20 | import java.util.Collection; 21 | 22 | /** 23 | * Filter to filter types or fields of object graphs passed to a SizeOf engine 24 | * 25 | * @author Chris Dennis 26 | * @see org.ehcache.sizeof.SizeOf 27 | */ 28 | public interface SizeOfFilter { 29 | 30 | /** 31 | * Returns the fields to walk and measure for a type 32 | * 33 | * @param klazz the type 34 | * @param fields the fields already "qualified" 35 | * @return the filtered Set 36 | */ 37 | Collection filterFields(Class klazz, Collection fields); 38 | 39 | /** 40 | * Checks whether the type needs to be filtered 41 | * 42 | * @param klazz the type 43 | * @return true, if to be filtered out 44 | */ 45 | boolean filterClass(Class klazz); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/filters/TypeFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.filters; 18 | 19 | import org.ehcache.sizeof.util.WeakIdentityConcurrentMap; 20 | 21 | import java.lang.reflect.Field; 22 | import java.util.Collection; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | import java.util.concurrent.ConcurrentMap; 25 | 26 | /** 27 | * @author Alex Snaps 28 | */ 29 | public class TypeFilter implements SizeOfFilter { 30 | 31 | private final WeakIdentityConcurrentMap, Object> classesIgnored = new WeakIdentityConcurrentMap<>(); 32 | private final WeakIdentityConcurrentMap, Object> superClasses = new WeakIdentityConcurrentMap<>(); 33 | private final WeakIdentityConcurrentMap, ConcurrentMap> fieldsIgnored = new WeakIdentityConcurrentMap<>(); 34 | 35 | @Override 36 | public Collection filterFields(final Class klazz, final Collection fields) { 37 | final ConcurrentMap fieldsToIgnore = fieldsIgnored.get(klazz); 38 | if (fieldsToIgnore != null) { 39 | fields.removeIf(fieldsToIgnore::containsKey); 40 | } 41 | return fields; 42 | } 43 | 44 | @Override 45 | public boolean filterClass(final Class klazz) { 46 | if (!classesIgnored.containsKey(klazz)) { 47 | for (Class aClass : superClasses.keySet()) { 48 | if (aClass.isAssignableFrom(klazz)) { 49 | classesIgnored.put(klazz, this); 50 | return false; 51 | } 52 | } 53 | return true; 54 | } else { 55 | return false; 56 | } 57 | } 58 | 59 | public void addClass(final Class classToFilterOut, final boolean strict) { 60 | if (!strict) { 61 | superClasses.putIfAbsent(classToFilterOut, this); 62 | } else { 63 | classesIgnored.put(classToFilterOut, this); 64 | } 65 | } 66 | 67 | public void addField(final Field fieldToFilterOut) { 68 | final Class klazz = fieldToFilterOut.getDeclaringClass(); 69 | ConcurrentMap fields = fieldsIgnored.get(klazz); 70 | if (fields == null) { 71 | fields = new ConcurrentHashMap<>(); 72 | final ConcurrentMap previous = fieldsIgnored.putIfAbsent(klazz, fields); 73 | if (previous != null) { 74 | fields = previous; 75 | } 76 | } 77 | fields.put(fieldToFilterOut, this); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/impl/AgentLoader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.io.File; 24 | import java.io.FileOutputStream; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.lang.instrument.Instrumentation; 28 | import java.lang.management.ManagementFactory; 29 | import java.lang.reflect.InvocationTargetException; 30 | import java.lang.reflect.Method; 31 | import java.net.URL; 32 | import java.net.URLClassLoader; 33 | import java.security.AccessController; 34 | import java.security.PrivilegedActionException; 35 | import java.security.PrivilegedExceptionAction; 36 | import java.util.ArrayList; 37 | import java.util.List; 38 | 39 | /** 40 | * This will try to load the agent using the Attach API of JDK6. 41 | * If you are on an older JDK (v5) you can still use the agent by adding the -javaagent:[pathTojar] to your VM 42 | * startup script 43 | * 44 | * @author Alex Snaps 45 | */ 46 | final class AgentLoader { 47 | 48 | private static final Logger LOGGER = LoggerFactory.getLogger(AgentLoader.class); 49 | 50 | private static final String SIZEOF_AGENT_CLASSNAME = "org.ehcache.sizeof.impl.SizeOfAgent"; 51 | private static final String VIRTUAL_MACHINE_CLASSNAME = "com.sun.tools.attach.VirtualMachine"; 52 | private static final Method VIRTUAL_MACHINE_ATTACH; 53 | private static final Method VIRTUAL_MACHINE_DETACH; 54 | private static final Method VIRTUAL_MACHINE_LOAD_AGENT; 55 | 56 | private static volatile Instrumentation instrumentation; 57 | 58 | static final String INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME = "org.ehcache.sizeof.agent.instrumentation"; 59 | 60 | static { 61 | Method attach = null; 62 | Method detach = null; 63 | Method loadAgent = null; 64 | try { 65 | Class virtualMachineClass = getVirtualMachineClass(); 66 | attach = virtualMachineClass.getMethod("attach", String.class); 67 | detach = virtualMachineClass.getMethod("detach"); 68 | loadAgent = virtualMachineClass.getMethod("loadAgent", String.class); 69 | } catch (Throwable e) { 70 | LOGGER.info("Unavailable or unrecognised attach API : {}", e.toString()); 71 | } 72 | VIRTUAL_MACHINE_ATTACH = attach; 73 | VIRTUAL_MACHINE_DETACH = detach; 74 | VIRTUAL_MACHINE_LOAD_AGENT = loadAgent; 75 | } 76 | 77 | private static Class getVirtualMachineClass() throws ClassNotFoundException { 78 | try { 79 | return AccessController.doPrivileged((PrivilegedExceptionAction>) () -> { 80 | try { 81 | return ClassLoader.getSystemClassLoader().loadClass(VIRTUAL_MACHINE_CLASSNAME); 82 | } catch (ClassNotFoundException cnfe) { 83 | for (File jar : getPossibleToolsJars()) { 84 | try { 85 | Class vmClass = new URLClassLoader(new URL[] { jar.toURI().toURL() }).loadClass(VIRTUAL_MACHINE_CLASSNAME); 86 | LOGGER.info("Located valid 'tools.jar' at '{}'", jar); 87 | return vmClass; 88 | } catch (Throwable t) { 89 | LOGGER.info("Exception while loading tools.jar from '{}': {}", jar, t); 90 | } 91 | } 92 | throw new ClassNotFoundException(VIRTUAL_MACHINE_CLASSNAME); 93 | } 94 | }); 95 | } catch (PrivilegedActionException pae) { 96 | Throwable actual = pae.getCause(); 97 | if (actual instanceof ClassNotFoundException) { 98 | throw (ClassNotFoundException)actual; 99 | } 100 | throw new AssertionError("Unexpected checked exception : " + actual); 101 | } 102 | } 103 | 104 | private static List getPossibleToolsJars() { 105 | List jars = new ArrayList<>(); 106 | 107 | File javaHome = new File(System.getProperty("java.home")); 108 | File jreSourced = new File(javaHome, "lib/tools.jar"); 109 | if (jreSourced.exists()) { 110 | jars.add(jreSourced); 111 | } 112 | if ("jre".equals(javaHome.getName())) { 113 | File jdkHome = new File(javaHome, "../"); 114 | File jdkSourced = new File(jdkHome, "lib/tools.jar"); 115 | if (jdkSourced.exists()) { 116 | jars.add(jdkSourced); 117 | } 118 | } 119 | return jars; 120 | } 121 | 122 | /** 123 | * Attempts to load the agent through the Attach API 124 | * 125 | * @return true if agent was loaded (which could have happened thought the -javaagent switch) 126 | */ 127 | static boolean loadAgent() { 128 | synchronized (AgentLoader.class.getName().intern()) { 129 | if (!agentIsAvailable() && VIRTUAL_MACHINE_LOAD_AGENT != null) { 130 | try { 131 | warnIfOSX(); 132 | String name = ManagementFactory.getRuntimeMXBean().getName(); 133 | Object vm = VIRTUAL_MACHINE_ATTACH.invoke(null, name.substring(0, name.indexOf('@'))); 134 | try { 135 | File agent = getAgentFile(); 136 | LOGGER.info("Trying to load agent @ {}", agent); 137 | if (agent != null) { 138 | VIRTUAL_MACHINE_LOAD_AGENT.invoke(vm, agent.getAbsolutePath()); 139 | } 140 | } finally { 141 | VIRTUAL_MACHINE_DETACH.invoke(vm); 142 | } 143 | } catch (InvocationTargetException ite) { 144 | Throwable cause = ite.getCause(); 145 | LOGGER.info("Failed to attach to VM and load the agent: {}: {}", cause.getClass(), cause.getMessage()); 146 | } catch (Throwable t) { 147 | LOGGER.info("Failed to attach to VM and load the agent: {}: {}", t.getClass(), t.getMessage()); 148 | } 149 | } 150 | final boolean b = agentIsAvailable(); 151 | if (b) { 152 | LOGGER.info("Agent successfully loaded and available!"); 153 | } 154 | 155 | return b; 156 | } 157 | } 158 | 159 | private static void warnIfOSX() { 160 | if (JvmInformation.isOSX() && System.getProperty("java.io.tmpdir") != null) { 161 | LOGGER.warn("Loading the SizeOfAgent will probably fail, as you are running on Apple OS X and have a value set for java.io.tmpdir\n" + 162 | "They both result in a bug, not yet fixed by Apple, that won't let us attach to the VM and load the agent.\n" + 163 | "Most probably, you'll also get a full thread-dump after this because of the failure... Nothing to worry about!\n" + 164 | "You can bypass trying to load the Agent entirely by setting the System property '" 165 | + AgentSizeOf.BYPASS_LOADING + "' to true"); 166 | } 167 | } 168 | 169 | private static File getAgentFile() throws IOException { 170 | URL agent = AgentLoader.class.getResource("sizeof-agent.jar"); 171 | if (agent == null) { 172 | return null; 173 | } else if (agent.getProtocol().equals("file")) { 174 | return new File(agent.getFile()); 175 | } else { 176 | File temp = File.createTempFile("ehcache-sizeof-agent", ".jar"); 177 | try (FileOutputStream fout = new FileOutputStream(temp); InputStream in = agent.openStream()) { 178 | byte[] buffer = new byte[1024]; 179 | while (true) { 180 | int read = in.read(buffer); 181 | if (read < 0) { 182 | break; 183 | } else { 184 | fout.write(buffer, 0, read); 185 | } 186 | } 187 | } finally { 188 | temp.deleteOnExit(); 189 | } 190 | LOGGER.info("Extracted agent jar to temporary file {}", temp); 191 | return temp; 192 | } 193 | } 194 | 195 | /** 196 | * Checks whether the agent is available 197 | * 198 | * @return true if available 199 | */ 200 | static boolean agentIsAvailable() { 201 | try { 202 | if (instrumentation == null) { 203 | instrumentation = (Instrumentation)System.getProperties().get(INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME); 204 | } 205 | if (instrumentation == null) { 206 | Class sizeOfAgentClass = ClassLoader.getSystemClassLoader().loadClass(SIZEOF_AGENT_CLASSNAME); 207 | Method getInstrumentationMethod = sizeOfAgentClass.getMethod("getInstrumentation"); 208 | instrumentation = (Instrumentation)getInstrumentationMethod.invoke(sizeOfAgentClass); 209 | } 210 | return instrumentation != null; 211 | } catch (SecurityException e) { 212 | LOGGER.warn("Couldn't access the system classloader because of the security policies applied by " + 213 | "the security manager. You either want to loosen these, so ClassLoader.getSystemClassLoader() and " + 214 | "reflection API calls are permitted or the sizing will be done using some other mechanism.\n" + 215 | "Alternatively, set the system property org.ehcache.sizeof.agent.instrumentationSystemProperty to true " + 216 | "to have the agent put the required instances in the System Properties for the loader to access."); 217 | return false; 218 | } catch (Throwable e) { 219 | return false; 220 | } 221 | } 222 | 223 | /** 224 | * Returns the size of this Java object as calculated by the loaded agent. 225 | * 226 | * @param obj object to be sized 227 | * @return size of the object in bytes 228 | */ 229 | static long agentSizeOf(Object obj) { 230 | if (instrumentation == null) { 231 | throw new UnsupportedOperationException("Sizeof agent is not available"); 232 | } 233 | return instrumentation.getObjectSize(obj); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/impl/AgentSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import org.ehcache.sizeof.SizeOf; 20 | import org.ehcache.sizeof.filters.SizeOfFilter; 21 | 22 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION; 23 | 24 | /** 25 | * SizeOf implementation that relies on a Java agent to be loaded to do the measurement 26 | * It will try to load the agent through the JDK6 Attach API if available 27 | * All it's constructor do throw UnsupportedOperationException if the agent isn't present or couldn't be loaded dynamically 28 | * 29 | * Inspired by Dr. Heinz Kabutz's Java Specialist Newsletter Issue #142 30 | * 31 | * @author Chris Dennis 32 | * @author Alex Snaps 33 | * 34 | * @link http://www.javaspecialists.eu/archive/Issue142.html 35 | */ 36 | public class AgentSizeOf extends SizeOf { 37 | 38 | /** 39 | * System property name to bypass attaching to the VM and loading of Java agent to measure Object sizes. 40 | */ 41 | public static final String BYPASS_LOADING = "org.ehcache.sizeof.AgentSizeOf.bypass"; 42 | 43 | private static final boolean AGENT_LOADED = !Boolean.getBoolean(BYPASS_LOADING) && AgentLoader.loadAgent(); 44 | 45 | /** 46 | * Builds a new SizeOf that will not filter fields and will cache reflected fields 47 | * 48 | * @throws UnsupportedOperationException If agent couldn't be loaded or isn't present 49 | * @see #AgentSizeOf(SizeOfFilter, boolean, boolean) 50 | */ 51 | public AgentSizeOf() throws UnsupportedOperationException { 52 | this(new PassThroughFilter()); 53 | } 54 | 55 | /** 56 | * Builds a new SizeOf that will filter fields according to the provided filter and will cache reflected fields 57 | * 58 | * @param filter The filter to apply 59 | * @throws UnsupportedOperationException If agent couldn't be loaded or isn't present 60 | * @see #AgentSizeOf(SizeOfFilter, boolean, boolean) 61 | * @see org.ehcache.sizeof.filters.SizeOfFilter 62 | */ 63 | public AgentSizeOf(SizeOfFilter filter) throws UnsupportedOperationException { 64 | this(filter, true, true); 65 | } 66 | 67 | /** 68 | * Builds a new SizeOf that will filter fields according to the provided filter 69 | * 70 | * @param filter The filter to apply 71 | * @param caching whether to cache reflected fields 72 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored 73 | * @throws UnsupportedOperationException If agent couldn't be loaded or isn't present 74 | * @see SizeOfFilter 75 | */ 76 | public AgentSizeOf(SizeOfFilter filter, boolean caching, boolean bypassFlyweight) throws UnsupportedOperationException { 77 | super(filter, caching, bypassFlyweight); 78 | if (!AGENT_LOADED) { 79 | throw new UnsupportedOperationException("Agent not available or loadable"); 80 | } 81 | } 82 | 83 | @Override 84 | public long sizeOf(Object obj) { 85 | final long measuredSize = AgentLoader.agentSizeOf(obj); 86 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), 87 | measuredSize + CURRENT_JVM_INFORMATION.getAgentSizeOfAdjustment()); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/impl/PassThroughFilter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import org.ehcache.sizeof.filters.SizeOfFilter; 20 | 21 | import java.lang.reflect.Field; 22 | import java.util.Collection; 23 | 24 | /** 25 | * @author Alex Snaps 26 | */ 27 | public class PassThroughFilter implements SizeOfFilter { 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | public Collection filterFields(Class klazz, Collection fields) { 33 | return fields; 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public boolean filterClass(Class klazz) { 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/impl/PrimitiveType.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION; 20 | 21 | /** 22 | * Primitive types in the VM type system and their sizes 23 | * 24 | * @author Alex Snaps 25 | */ 26 | enum PrimitiveType { 27 | 28 | /** 29 | * boolean.class 30 | */ 31 | BOOLEAN(boolean.class, 1), 32 | /** 33 | * byte.class 34 | */ 35 | BYTE(byte.class, 1), 36 | /** 37 | * char.class 38 | */ 39 | CHAR(char.class, 2), 40 | /** 41 | * short.class 42 | */ 43 | SHORT(short.class, 2), 44 | /** 45 | * int.class 46 | */ 47 | INT(int.class, 4), 48 | /** 49 | * float.class 50 | */ 51 | FLOAT(float.class, 4), 52 | /** 53 | * double.class 54 | */ 55 | DOUBLE(double.class, 8), 56 | /** 57 | * long.class 58 | */ 59 | LONG(long.class, 8); 60 | 61 | private final Class type; 62 | private final int size; 63 | 64 | 65 | PrimitiveType(Class type, int size) { 66 | this.type = type; 67 | this.size = size; 68 | } 69 | 70 | /** 71 | * Returns the size in memory this type occupies 72 | * 73 | * @return size in bytes 74 | */ 75 | public int getSize() { 76 | return size; 77 | } 78 | 79 | /** 80 | * The representing type 81 | * 82 | * @return the type 83 | */ 84 | public Class getType() { 85 | return type; 86 | } 87 | 88 | /** 89 | * The size of a pointer 90 | * 91 | * @return size in bytes 92 | */ 93 | public static int getReferenceSize() { 94 | return CURRENT_JVM_INFORMATION.getJavaPointerSize(); 95 | } 96 | 97 | /** 98 | * The size on an array 99 | * 100 | * @return size in bytes 101 | */ 102 | public static long getArraySize() { 103 | return CURRENT_JVM_INFORMATION.getObjectHeaderSize() + INT.getSize(); 104 | } 105 | 106 | /** 107 | * Finds the matching PrimitiveType for a type 108 | * 109 | * @param type the type to find the PrimitiveType for 110 | * @return the PrimitiveType instance or null if none found 111 | */ 112 | public static PrimitiveType forType(final Class type) { 113 | for (PrimitiveType primitiveType : values()) { 114 | if (primitiveType.getType() == type) { 115 | return primitiveType; 116 | } 117 | } 118 | return null; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/impl/ReflectionSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import org.ehcache.sizeof.SizeOf; 20 | import org.ehcache.sizeof.filters.SizeOfFilter; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import java.lang.reflect.Array; 25 | import java.lang.reflect.Field; 26 | import java.lang.reflect.Modifier; 27 | import java.util.ArrayDeque; 28 | import java.util.Deque; 29 | 30 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION; 31 | 32 | /** 33 | * SizeOf that uses reflection to measure on heap size of object graphs 34 | * Inspired by Dr. Heinz Kabutz's Java Specialist Newsletter Issue #78 35 | * 36 | * @author Alex Snaps 37 | * @author Chris Dennis 38 | * 39 | * @link http://www.javaspecialists.eu/archive/Issue078.html 40 | */ 41 | public class ReflectionSizeOf extends SizeOf { 42 | 43 | private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionSizeOf.class); 44 | 45 | /** 46 | * Builds a new SizeOf that will not filter fields and will cache reflected fields 47 | * 48 | * @see #ReflectionSizeOf(SizeOfFilter, boolean, boolean) 49 | */ 50 | public ReflectionSizeOf() { 51 | this(new PassThroughFilter()); 52 | } 53 | 54 | /** 55 | * Builds a new SizeOf that will filter fields and will cache reflected fields 56 | * 57 | * @param fieldFilter The filter to apply 58 | * @see #ReflectionSizeOf(SizeOfFilter, boolean, boolean) 59 | * @see SizeOfFilter 60 | */ 61 | public ReflectionSizeOf(SizeOfFilter fieldFilter) { 62 | this(fieldFilter, true, true); 63 | } 64 | 65 | /** 66 | * Builds a new SizeOf that will filter fields 67 | * 68 | * @param fieldFilter The filter to apply 69 | * @param caching Whether to cache reflected fields 70 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored 71 | * @see SizeOfFilter 72 | */ 73 | public ReflectionSizeOf(SizeOfFilter fieldFilter, boolean caching, boolean bypassFlyweight) { 74 | super(fieldFilter, caching, bypassFlyweight); 75 | 76 | if (!CURRENT_JVM_INFORMATION.supportsReflectionSizeOf()) { 77 | LOGGER.warn("ReflectionSizeOf is not always accurate on the JVM (" + CURRENT_JVM_INFORMATION.getJvmDescription() + 78 | "). Please consider enabling AgentSizeOf."); 79 | } 80 | } 81 | 82 | /** 83 | * {@inheritDoc} 84 | */ 85 | @Override 86 | public long sizeOf(Object obj) { 87 | if (obj == null) { 88 | return 0; 89 | } 90 | 91 | Class aClass = obj.getClass(); 92 | if (aClass.isArray()) { 93 | return guessArraySize(obj); 94 | } else { 95 | long size = CURRENT_JVM_INFORMATION.getObjectHeaderSize(); 96 | 97 | Deque> classStack = new ArrayDeque<>(); 98 | for (Class klazz = aClass; klazz != null; klazz = klazz.getSuperclass()) { 99 | classStack.push(klazz); 100 | } 101 | 102 | while (!classStack.isEmpty()) { 103 | Class klazz = classStack.pop(); 104 | 105 | //assuming default class layout 106 | int oops = 0; 107 | int doubles = 0; 108 | int words = 0; 109 | int shorts = 0; 110 | int bytes = 0; 111 | for (Field f : klazz.getDeclaredFields()) { 112 | if (Modifier.isStatic(f.getModifiers())) { 113 | continue; 114 | } 115 | if (f.getType().isPrimitive()) { 116 | switch (PrimitiveType.forType(f.getType())) { 117 | case BOOLEAN: 118 | case BYTE: 119 | bytes++; 120 | break; 121 | case SHORT: 122 | case CHAR: 123 | shorts++; 124 | break; 125 | case INT: 126 | case FLOAT: 127 | words++; 128 | break; 129 | case DOUBLE: 130 | case LONG: 131 | doubles++; 132 | break; 133 | default: 134 | throw new AssertionError(); 135 | } 136 | } else { 137 | oops++; 138 | } 139 | } 140 | if (doubles > 0 && (size % PrimitiveType.LONG.getSize()) != 0) { 141 | long length = PrimitiveType.LONG.getSize() - (size % PrimitiveType.LONG.getSize()); 142 | size += PrimitiveType.LONG.getSize() - (size % PrimitiveType.LONG.getSize()); 143 | 144 | while (length >= PrimitiveType.INT.getSize() && words > 0) { 145 | length -= PrimitiveType.INT.getSize(); 146 | words--; 147 | } 148 | while (length >= PrimitiveType.SHORT.getSize() && shorts > 0) { 149 | length -= PrimitiveType.SHORT.getSize(); 150 | shorts--; 151 | } 152 | while (length >= PrimitiveType.BYTE.getSize() && bytes > 0) { 153 | length -= PrimitiveType.BYTE.getSize(); 154 | bytes--; 155 | } 156 | while (length >= PrimitiveType.getReferenceSize() && oops > 0) { 157 | length -= PrimitiveType.getReferenceSize(); 158 | oops--; 159 | } 160 | } 161 | size += PrimitiveType.DOUBLE.getSize() * doubles; 162 | size += PrimitiveType.INT.getSize() * words; 163 | size += PrimitiveType.SHORT.getSize() * shorts; 164 | size += PrimitiveType.BYTE.getSize() * bytes; 165 | 166 | if (oops > 0) { 167 | if ((size % PrimitiveType.getReferenceSize()) != 0) { 168 | size += PrimitiveType.getReferenceSize() - (size % PrimitiveType.getReferenceSize()); 169 | } 170 | size += oops * PrimitiveType.getReferenceSize(); 171 | } 172 | 173 | if ((doubles + words + shorts + bytes + oops) > 0 && (size % PrimitiveType.getReferenceSize()) != 0) { 174 | size += PrimitiveType.getReferenceSize() - (size % PrimitiveType.getReferenceSize()); 175 | } 176 | } 177 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) { 178 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment()); 179 | } 180 | return Math.max(size, CURRENT_JVM_INFORMATION.getMinimumObjectSize()); 181 | } 182 | } 183 | 184 | private long guessArraySize(Object obj) { 185 | long size = PrimitiveType.getArraySize(); 186 | int length = Array.getLength(obj); 187 | if (length != 0) { 188 | Class arrayElementClazz = obj.getClass().getComponentType(); 189 | if (arrayElementClazz.isPrimitive()) { 190 | size += length * PrimitiveType.forType(arrayElementClazz).getSize(); 191 | } else { 192 | size += length * PrimitiveType.getReferenceSize(); 193 | } 194 | } 195 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) { 196 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment()); 197 | } 198 | return Math.max(size, CURRENT_JVM_INFORMATION.getMinimumObjectSize()); 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/impl/SizeOfAgent.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import java.lang.instrument.Instrumentation; 20 | 21 | /** 22 | * @author Alex Snaps 23 | */ 24 | public class SizeOfAgent { 25 | 26 | private static volatile Instrumentation instrumentation; 27 | private static final String NO_INSTRUMENTATION_SYSTEM_PROPERTY_NAME = "org.ehcache.sizeof.agent.instrumentationSystemProperty"; 28 | 29 | public static void premain(String options, Instrumentation inst) { 30 | SizeOfAgent.instrumentation = inst; 31 | registerSystemProperty(); 32 | } 33 | 34 | public static void agentmain(String options, Instrumentation inst) { 35 | SizeOfAgent.instrumentation = inst; 36 | registerSystemProperty(); 37 | } 38 | 39 | private static void registerSystemProperty() { 40 | if (Boolean.getBoolean(NO_INSTRUMENTATION_SYSTEM_PROPERTY_NAME)) { 41 | System.getProperties().put(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME, instrumentation); 42 | } 43 | } 44 | 45 | public static Instrumentation getInstrumentation() { 46 | return instrumentation; 47 | } 48 | 49 | private SizeOfAgent() { 50 | //not instantiable 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/impl/UnsafeSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import sun.misc.Unsafe; 20 | import org.ehcache.sizeof.SizeOf; 21 | import org.ehcache.sizeof.filters.SizeOfFilter; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import java.lang.reflect.Array; 26 | import java.lang.reflect.Field; 27 | import java.lang.reflect.Modifier; 28 | 29 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION; 30 | 31 | /** 32 | * {@link sun.misc.Unsafe#theUnsafe} based sizeOf measurement 33 | * All constructors will throw UnsupportedOperationException if theUnsafe isn't accessible on this platform 34 | * 35 | * @author Chris Dennis 36 | */ 37 | @SuppressWarnings("restriction") 38 | public class UnsafeSizeOf extends SizeOf { 39 | 40 | 41 | private static final Logger LOGGER = LoggerFactory.getLogger(UnsafeSizeOf.class); 42 | 43 | private static final Unsafe UNSAFE; 44 | 45 | static { 46 | Unsafe unsafe; 47 | try { 48 | Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); 49 | unsafeField.setAccessible(true); 50 | unsafe = (Unsafe)unsafeField.get(null); 51 | } catch (Throwable t) { 52 | unsafe = null; 53 | } 54 | UNSAFE = unsafe; 55 | } 56 | 57 | /** 58 | * Builds a new SizeOf that will not filter fields and will cache reflected fields 59 | * 60 | * @throws UnsupportedOperationException If Unsafe isn't accessible 61 | * @see #UnsafeSizeOf(org.ehcache.sizeof.filters.SizeOfFilter, boolean, boolean) 62 | */ 63 | public UnsafeSizeOf() throws UnsupportedOperationException { 64 | this(new PassThroughFilter()); 65 | } 66 | 67 | /** 68 | * Builds a new SizeOf that will filter fields according to the provided filter and will cache reflected fields 69 | * 70 | * @param filter The filter to apply 71 | * @throws UnsupportedOperationException If Unsafe isn't accessible 72 | * @see #UnsafeSizeOf(org.ehcache.sizeof.filters.SizeOfFilter, boolean, boolean) 73 | * @see org.ehcache.sizeof.filters.SizeOfFilter 74 | */ 75 | public UnsafeSizeOf(SizeOfFilter filter) throws UnsupportedOperationException { 76 | this(filter, true, true); 77 | } 78 | 79 | /** 80 | * Builds a new SizeOf that will filter fields according to the provided filter 81 | * 82 | * @param filter The filter to apply 83 | * @param caching whether to cache reflected fields 84 | * @param bypassFlyweight whether "Flyweight Objects" are to be ignored 85 | * @throws UnsupportedOperationException If Unsafe isn't accessible 86 | * @see SizeOfFilter 87 | */ 88 | public UnsafeSizeOf(SizeOfFilter filter, boolean caching, boolean bypassFlyweight) throws UnsupportedOperationException { 89 | super(filter, caching, bypassFlyweight); 90 | if (UNSAFE == null) { 91 | throw new UnsupportedOperationException("sun.misc.Unsafe instance not accessible"); 92 | } 93 | 94 | if (!CURRENT_JVM_INFORMATION.supportsUnsafeSizeOf()) { 95 | LOGGER.warn("UnsafeSizeOf is not always accurate on the JVM (" + CURRENT_JVM_INFORMATION.getJvmDescription() + 96 | "). Please consider enabling AgentSizeOf."); 97 | } 98 | 99 | } 100 | 101 | /** 102 | * {@inheritDoc} 103 | */ 104 | @Override 105 | public long sizeOf(Object obj) { 106 | if (obj.getClass().isArray()) { 107 | Class klazz = obj.getClass(); 108 | int base = UNSAFE.arrayBaseOffset(klazz); 109 | int scale = UNSAFE.arrayIndexScale(klazz); 110 | long size = base + (scale * Array.getLength(obj)); 111 | size += CURRENT_JVM_INFORMATION.getFieldOffsetAdjustment(); 112 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) { 113 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment()); 114 | } 115 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), size); 116 | } else { 117 | for (Class klazz = obj.getClass(); klazz != null; klazz = klazz.getSuperclass()) { 118 | long lastFieldOffset = -1; 119 | for (Field f : klazz.getDeclaredFields()) { 120 | if (!Modifier.isStatic(f.getModifiers())) { 121 | lastFieldOffset = Math.max(lastFieldOffset, UNSAFE.objectFieldOffset(f)); 122 | } 123 | } 124 | if (lastFieldOffset > 0) { 125 | lastFieldOffset += CURRENT_JVM_INFORMATION.getFieldOffsetAdjustment(); 126 | lastFieldOffset += 1; 127 | if ((lastFieldOffset % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) { 128 | lastFieldOffset += CURRENT_JVM_INFORMATION.getObjectAlignment() - 129 | (lastFieldOffset % CURRENT_JVM_INFORMATION.getObjectAlignment()); 130 | } 131 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), lastFieldOffset); 132 | } 133 | } 134 | 135 | long size = CURRENT_JVM_INFORMATION.getObjectHeaderSize(); 136 | if ((size % CURRENT_JVM_INFORMATION.getObjectAlignment()) != 0) { 137 | size += CURRENT_JVM_INFORMATION.getObjectAlignment() - (size % CURRENT_JVM_INFORMATION.getObjectAlignment()); 138 | } 139 | return Math.max(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), size); 140 | } 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/org/ehcache/sizeof/util/WeakIdentityConcurrentMap.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.util; 18 | 19 | import java.lang.ref.Reference; 20 | import java.lang.ref.ReferenceQueue; 21 | import java.lang.ref.WeakReference; 22 | import java.util.HashSet; 23 | import java.util.Set; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | import java.util.concurrent.ConcurrentMap; 26 | 27 | /** 28 | * A poor man's implementation of a WeakIdentityConcurrentMap to hold the CacheManager associated ExecutorServices 29 | * 30 | * @param The key type 31 | * @param The value type 32 | * @author Alex Snaps 33 | */ 34 | public final class WeakIdentityConcurrentMap { 35 | 36 | private final ConcurrentMap, V> map = new ConcurrentHashMap<>(); 37 | private final ReferenceQueue queue = new ReferenceQueue<>(); 38 | 39 | private final CleanUpTask cleanUpTask; 40 | 41 | /** 42 | * Constructor 43 | */ 44 | public WeakIdentityConcurrentMap() { 45 | this(null); 46 | } 47 | 48 | /** 49 | * Constructor 50 | * 51 | * @param cleanUpTask task cleaning up references 52 | */ 53 | public WeakIdentityConcurrentMap(final CleanUpTask cleanUpTask) { 54 | this.cleanUpTask = cleanUpTask; 55 | } 56 | 57 | /** 58 | * Puts into the underlying 59 | * 60 | * @param key key with which the specified value is to be associated 61 | * @param value value to be associated with the specified key 62 | * @return the previous value associated with key, or 63 | * null if there was no mapping for key. 64 | * (A null return can also indicate that the map 65 | * previously associated null with key, 66 | * if the implementation supports null values.) 67 | */ 68 | public V put(K key, V value) { 69 | cleanUp(); 70 | return map.put(new IdentityWeakReference<>(key, queue), value); 71 | } 72 | 73 | /** 74 | * Remove from the underlying 75 | * 76 | * @param key key whose mapping is to be removed from the map 77 | * @return the previous value associated with key, or 78 | * null if there was no mapping for key. 79 | */ 80 | public V remove(K key) { 81 | cleanUp(); 82 | return map.remove(new IdentityWeakReference<>(key, queue)); 83 | } 84 | 85 | /** 86 | * {@inheritDoc} 87 | */ 88 | @Override 89 | public String toString() { 90 | cleanUp(); 91 | return map.toString(); 92 | } 93 | 94 | /** 95 | * Puts into the underlying 96 | * 97 | * @param key key with which the specified value is to be associated 98 | * @param value value to be associated with the specified key 99 | * @return the previous value associated with the specified key, or 100 | * {@code null} if there was no mapping for the key. 101 | * (A {@code null} return can also indicate that the map 102 | * previously associated {@code null} with the key, 103 | * if the implementation supports null values.) 104 | */ 105 | public V putIfAbsent(K key, V value) { 106 | cleanUp(); 107 | return map.putIfAbsent(new IdentityWeakReference<>(key, queue), value); 108 | } 109 | 110 | /** 111 | * @param key the key whose associated value is to be returned 112 | * @return the value to which the specified key is mapped, or 113 | * {@code null} if this map contains no mapping for the key 114 | */ 115 | public V get(K key) { 116 | cleanUp(); 117 | return map.get(new IdentityWeakReference<>(key)); 118 | } 119 | 120 | /** 121 | * 122 | */ 123 | public void cleanUp() { 124 | 125 | Reference reference; 126 | while ((reference = queue.poll()) != null) { 127 | final V value = map.remove(reference); 128 | if (cleanUpTask != null && value != null) { 129 | cleanUpTask.cleanUp(value); 130 | } 131 | } 132 | } 133 | 134 | /** 135 | * @return a set view of the keys contained in this map 136 | */ 137 | public Set keySet() { 138 | cleanUp(); 139 | K k; 140 | final HashSet ks = new HashSet<>(); 141 | for (WeakReference weakReference : map.keySet()) { 142 | k = weakReference.get(); 143 | if (k != null) { 144 | ks.add(k); 145 | } 146 | } 147 | return ks; 148 | } 149 | 150 | public boolean containsKey(final K key) { 151 | cleanUp(); 152 | return map.containsKey(new IdentityWeakReference<>(key)); 153 | } 154 | 155 | /** 156 | * @param 157 | */ 158 | private static final class IdentityWeakReference extends WeakReference { 159 | 160 | private final int hashCode; 161 | 162 | /** 163 | * @param reference the referenced object 164 | */ 165 | IdentityWeakReference(T reference) { 166 | this(reference, null); 167 | } 168 | 169 | /** 170 | * @param reference the references object 171 | * @param referenceQueue the reference queue where references are kept 172 | */ 173 | IdentityWeakReference(T reference, ReferenceQueue referenceQueue) { 174 | super(reference, referenceQueue); 175 | this.hashCode = (reference == null) ? 0 : System.identityHashCode(reference); 176 | } 177 | 178 | /** 179 | * {@inheritDoc} 180 | */ 181 | @Override 182 | public String toString() { 183 | return String.valueOf(get()); 184 | } 185 | 186 | /** 187 | * {@inheritDoc} 188 | */ 189 | @Override 190 | public boolean equals(Object o) { 191 | if (this == o) { 192 | return true; 193 | } 194 | if (!(o instanceof IdentityWeakReference)) { 195 | return false; 196 | } else { 197 | IdentityWeakReference wr = (IdentityWeakReference)o; 198 | Object got = get(); 199 | return (got != null && got == wr.get()); 200 | } 201 | } 202 | 203 | /** 204 | * {@inheritDoc} 205 | */ 206 | @Override 207 | public int hashCode() { 208 | return hashCode; 209 | } 210 | } 211 | 212 | /** 213 | * @param 214 | */ 215 | public interface CleanUpTask { 216 | 217 | /** 218 | * @param object object to cleanup 219 | */ 220 | void cleanUp(T object); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/test/java/com/terracotta/ehcache/special/annotation/IgnoreSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.terracotta.ehcache.special.annotation; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE }) 26 | public @interface IgnoreSizeOf { 27 | boolean inherited() default true; 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/terracotta/ehcache/special/annotation/IgnoreSizeOffff.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.terracotta.ehcache.special.annotation; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE }) 26 | public @interface IgnoreSizeOffff { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/com/terracotta/ehcache/special/annotation/no/inherited/IgnoreSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.terracotta.ehcache.special.annotation.no.inherited; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE }) 26 | public @interface IgnoreSizeOf { 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/com/terracotta/special/annotation/IgnoreSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.terracotta.special.annotation; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE }) 26 | public @interface IgnoreSizeOf { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/AbstractSizeOfTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import java.lang.management.ManagementFactory; 20 | 21 | import javax.management.MBeanServer; 22 | import javax.management.ObjectName; 23 | import javax.management.openmbean.CompositeData; 24 | 25 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION; 26 | 27 | /** 28 | * @author Alex Snaps 29 | */ 30 | abstract class AbstractSizeOfTest { 31 | 32 | protected static final boolean COMPRESSED_OOPS; 33 | protected static final boolean HOTSPOT_CMS; 34 | protected static final boolean IS_HOTSPOT; 35 | protected static final boolean IS_JROCKIT; 36 | protected static final boolean IS_IBM; 37 | protected static final boolean IS_64_BIT; 38 | 39 | static { 40 | String value = getVmOptionValue("UseCompressedOops"); 41 | if (value == null) { 42 | System.err.println("Could not detect compressed-oops status assuming: false"); 43 | COMPRESSED_OOPS = false; 44 | } else { 45 | COMPRESSED_OOPS = Boolean.valueOf(value); 46 | } 47 | 48 | HOTSPOT_CMS = CURRENT_JVM_INFORMATION.getMinimumObjectSize() > CURRENT_JVM_INFORMATION.getObjectAlignment(); 49 | 50 | IS_64_BIT = System.getProperty("sun.arch.data.model").equals("64"); 51 | 52 | IS_HOTSPOT = System.getProperty("java.vm.name", "").toLowerCase().contains("hotspot"); 53 | 54 | IS_JROCKIT = System.getProperty("jrockit.version") != null || 55 | System.getProperty("java.vm.name", "").toLowerCase().contains("jrockit"); 56 | 57 | IS_IBM = System.getProperty("java.vm.name", "").contains("IBM") && 58 | System.getProperty("java.vm.vendor").contains("IBM"); 59 | } 60 | 61 | private static String getVmOptionValue(String name) { 62 | try { 63 | MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 64 | Object vmOption = server.invoke(ObjectName.getInstance("com.sun.management:type=HotSpotDiagnostic"), "getVMOption", new Object[] { name }, new String[] { "java.lang.String" }); 65 | return (String)((CompositeData)vmOption).get("value"); 66 | } catch (Throwable t) { 67 | return null; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/ConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter; 20 | import org.hamcrest.CoreMatchers; 21 | import org.junit.Test; 22 | 23 | import static org.hamcrest.CoreMatchers.is; 24 | import static org.hamcrest.CoreMatchers.sameInstance; 25 | import static org.junit.Assert.assertThat; 26 | 27 | /** 28 | * @author Alex Snaps 29 | */ 30 | public class ConfigurationTest { 31 | 32 | @Test 33 | public void testBuilderBuilds() { 34 | final AnnotationSizeOfFilter filter = new AnnotationSizeOfFilter(); 35 | final Configuration configuration = new Configuration.Builder().abort(true) 36 | .maxDepth(666) 37 | .silent(true) 38 | .addFilter(filter) 39 | .build(); 40 | assertThat(configuration.getFilters().length, is(1)); 41 | assertThat(configuration.getFilters()[0], CoreMatchers.sameInstance(filter)); 42 | assertThat(configuration.isAbort(), is(true)); 43 | assertThat(configuration.isSilent(), is(true)); 44 | assertThat(configuration.getMaxDepth(), is(666)); 45 | } 46 | 47 | @Test 48 | public void testBuilderCopies() { 49 | final AnnotationSizeOfFilter filter = new AnnotationSizeOfFilter(); 50 | final Configuration template = new Configuration.Builder().abort(true) 51 | .maxDepth(666) 52 | .silent(true) 53 | .addFilter(filter) 54 | .build(); 55 | final Configuration cfg = new Configuration.Builder(template).build(); 56 | assertThat(cfg.getMaxDepth(), is(template.getMaxDepth())); 57 | assertThat(cfg.isSilent(), is(template.isSilent())); 58 | assertThat(cfg.isAbort(), is(template.isAbort())); 59 | assertThat(cfg.getFilters()[0], sameInstance(template.getFilters()[0])); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/CrossCheckingSizeOf.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.SizeOfFilter; 20 | import org.ehcache.sizeof.impl.AgentSizeOf; 21 | import org.ehcache.sizeof.impl.PassThroughFilter; 22 | import org.ehcache.sizeof.impl.ReflectionSizeOf; 23 | import org.ehcache.sizeof.impl.UnsafeSizeOf; 24 | 25 | import java.lang.reflect.Array; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | 29 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION; 30 | 31 | /** 32 | * @author Alex Snaps 33 | */ 34 | public class CrossCheckingSizeOf extends SizeOf { 35 | 36 | private final List engines; 37 | 38 | public CrossCheckingSizeOf() { 39 | this(new PassThroughFilter()); 40 | } 41 | 42 | public CrossCheckingSizeOf(boolean bypassFlyweight) { 43 | this(new PassThroughFilter(), true, bypassFlyweight); 44 | } 45 | 46 | public CrossCheckingSizeOf(SizeOfFilter filter) { 47 | this(filter, true, true); 48 | } 49 | 50 | public CrossCheckingSizeOf(SizeOfFilter filter, boolean caching, boolean bypassFlyweight) { 51 | super(filter, caching, bypassFlyweight); 52 | engines = new ArrayList<>(); 53 | 54 | try { 55 | engines.add(new AgentSizeOf()); 56 | } catch (UnsupportedOperationException usoe) { 57 | System.err.println("Not using AgentSizeOf: " + usoe); 58 | } 59 | try { 60 | engines.add(new UnsafeSizeOf()); 61 | } catch (UnsupportedOperationException usoe) { 62 | System.err.println("Not using UnsafeSizeOf: " + usoe); 63 | } 64 | if (CURRENT_JVM_INFORMATION.supportsReflectionSizeOf()) { 65 | try { 66 | engines.add(new ReflectionSizeOf()); 67 | } catch (UnsupportedOperationException usoe) { 68 | System.err.println("Not using ReflectionSizeOf: " + usoe); 69 | } 70 | } else { 71 | System.err.println(CURRENT_JVM_INFORMATION.getJvmDescription() + " detected: not using ReflectionSizeOf"); 72 | } 73 | 74 | if (engines.isEmpty()) { 75 | throw new AssertionError("No SizeOf engines available"); 76 | } 77 | } 78 | 79 | @Override 80 | public long sizeOf(Object obj) { 81 | long[] values = new long[engines.size()]; 82 | for (int i = 0; i < engines.size(); i++) { 83 | values[i] = engines.get(i).sizeOf(obj); 84 | } 85 | for (long value : values) { 86 | if (values[0] != value) { 87 | StringBuilder sb = new StringBuilder("Values do not match for "); 88 | sb.append(obj.getClass()); 89 | if (obj.getClass().isArray()) { 90 | sb.append(" length:").append(Array.getLength(obj)); 91 | } 92 | sb.append(" - "); 93 | for (int i = 0; i < engines.size(); i++) { 94 | sb.append(engines.get(i).getClass().getSimpleName()).append(":").append(values[i]); 95 | if (i != engines.size() - 1) { 96 | sb.append(", "); 97 | } 98 | } 99 | throw new AssertionError(sb.toString()); 100 | } 101 | } 102 | 103 | return values[0]; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/CrossCheckingSizeOfIT.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.Collection; 22 | import java.util.Collections; 23 | import java.util.Comparator; 24 | import java.util.HashSet; 25 | import java.util.List; 26 | import java.util.Set; 27 | 28 | import org.junit.Test; 29 | import org.objectweb.asm.ClassWriter; 30 | import org.objectweb.asm.MethodVisitor; 31 | import org.objectweb.asm.Type; 32 | 33 | import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; 34 | import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS; 35 | import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 36 | import static org.objectweb.asm.Opcodes.ALOAD; 37 | import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 38 | import static org.objectweb.asm.Opcodes.RETURN; 39 | import static org.objectweb.asm.Opcodes.V1_6; 40 | 41 | /** 42 | * 43 | * @author cdennis 44 | */ 45 | public class CrossCheckingSizeOfIT { 46 | 47 | private static final Comparator> COMPARATOR = Comparator.comparing(Class::getName); 48 | 49 | private static final Collection> FIELD_TYPES = Arrays.asList( 50 | Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Object.class); 51 | 52 | @Test 53 | public void testSingleClass() throws Exception { 54 | CrossCheckingSizeOf sizeOf = new CrossCheckingSizeOf(); 55 | for (int i = 0; i <= 9; i++) { 56 | for (List> testcase : permutations(FIELD_TYPES, COMPARATOR, i)) { 57 | sizeOf.sizeOf(generateClassHierarchy(Collections.singletonList(testcase)).newInstance()); 58 | } 59 | } 60 | } 61 | 62 | @Test 63 | public void testTwoClasses() throws Exception { 64 | CrossCheckingSizeOf sizeOf = new CrossCheckingSizeOf(); 65 | for (int i = 0; i <= 5; i++) { 66 | for (int j = 0; j <= 5; j++) { 67 | for (List> superklazz : permutations(FIELD_TYPES, COMPARATOR, i)) { 68 | for (List> klazz : permutations(FIELD_TYPES, COMPARATOR, j)) { 69 | sizeOf.sizeOf(generateClassHierarchy(Arrays.asList(superklazz, klazz)).newInstance()); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | private static Set> permutations(Collection choices, Comparator comparator, int count) { 77 | if (count < 0) { 78 | throw new IllegalArgumentException(); 79 | } else if (count == 0) { 80 | return Collections.singleton(Collections.emptyList()); 81 | } else { 82 | Set> subperms = permutations(choices, comparator, count -1); 83 | Set> perms = new HashSet<>(subperms.size() * choices.size()); 84 | for (List sub : subperms) { 85 | for (T element : choices) { 86 | List p = new ArrayList<>(sub.size() + 1); 87 | p.addAll(sub); 88 | p.add(element); 89 | p.sort(comparator); 90 | perms.add(p); 91 | } 92 | } 93 | return perms; 94 | } 95 | } 96 | 97 | private static Class generateClassHierarchy(List>> classes) { 98 | TestClassLoader loader = new TestClassLoader(); 99 | 100 | String superClassDesc = "java/lang/Object"; 101 | int classIndex = 0; 102 | 103 | for (List> fields : classes) { 104 | String classDesc = "A" + classIndex++; 105 | ClassWriter cw = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES); 106 | cw.visit(V1_6, ACC_PUBLIC, classDesc, null, superClassDesc, null); 107 | MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); 108 | mv.visitCode(); 109 | mv.visitVarInsn(ALOAD, 0); 110 | mv.visitMethodInsn(INVOKESPECIAL, superClassDesc, "", "()V", false); 111 | mv.visitInsn(RETURN); 112 | mv.visitMaxs(1, 1); 113 | mv.visitEnd(); 114 | 115 | int fieldIndex = 0; 116 | for (Class field : fields) { 117 | cw.visitField(ACC_PUBLIC, "a" + fieldIndex++, Type.getDescriptor(field), null, null); 118 | } 119 | cw.visitEnd(); 120 | loader.defineClass(classDesc, cw.toByteArray()); 121 | superClassDesc = classDesc; 122 | } 123 | 124 | try { 125 | return loader.loadClass("A" + (classIndex - 1)); 126 | } catch (ClassNotFoundException | NoClassDefFoundError e) { 127 | throw new AssertionError(e); 128 | } 129 | } 130 | 131 | private static class TestClassLoader extends ClassLoader { 132 | 133 | public void defineClass(String name, byte[] classbytes) { 134 | defineClass(name, classbytes, 0, classbytes.length); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/FilteredSizeOfTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.annotations.IgnoreSizeOf; 20 | import org.ehcache.sizeof.filteredtest.AnnotationFilteredPackage; 21 | import org.ehcache.sizeof.filteredtest.custom.CustomAnnotationFilteredPackage; 22 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter; 23 | import org.junit.BeforeClass; 24 | import org.junit.Test; 25 | 26 | import static org.hamcrest.CoreMatchers.allOf; 27 | import static org.hamcrest.Matchers.greaterThan; 28 | import static org.hamcrest.Matchers.lessThan; 29 | import static org.hamcrest.core.IsEqual.equalTo; 30 | import static org.junit.Assert.assertThat; 31 | 32 | /** 33 | * @author Alex Snaps 34 | */ 35 | public class FilteredSizeOfTest extends AbstractSizeOfTest { 36 | private static long deepSizeOf(SizeOf sizeOf, Object... obj) { 37 | return sizeOf.deepSizeOf(obj); 38 | } 39 | 40 | @BeforeClass 41 | public static void setup() { 42 | deepSizeOf(new CrossCheckingSizeOf(), new Object()); 43 | System.out.println("Testing for a " + System.getProperty("java.version") + " JDK " 44 | + ") on a " + System.getProperty("sun.arch.data.model") + "-bit VM " 45 | + "(compressed-oops: " + COMPRESSED_OOPS 46 | + ", Hotspot CMS: " + HOTSPOT_CMS 47 | + ")"); 48 | } 49 | 50 | @Test 51 | public void testAnnotationFiltering() throws Exception { 52 | SizeOf sizeOf = new CrossCheckingSizeOf(new AnnotationSizeOfFilter()); 53 | 54 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredField()), allOf(greaterThan(128L), lessThan(16 * 1024L))); 55 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredClass()), equalTo(0L)); 56 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredPackage()), equalTo(0L)); 57 | 58 | assertThat(deepSizeOf(sizeOf, new AnnotationFilteredFieldSubclass()), allOf(greaterThan(128L), lessThan(16 * 1024L))); 59 | 60 | long emptyReferrerSize = deepSizeOf(sizeOf, new Referrer(null)); 61 | assertThat(deepSizeOf(sizeOf, new Referrer(new AnnotationFilteredClass())), equalTo(emptyReferrerSize)); 62 | assertThat(deepSizeOf(sizeOf, new Referrer(new AnnotationFilteredPackage())), equalTo(emptyReferrerSize)); 63 | assertThat(deepSizeOf(sizeOf, new Parent()), equalTo(0L)); 64 | assertThat(deepSizeOf(sizeOf, new Child()), equalTo(0L)); 65 | assertThat(deepSizeOf(sizeOf, new ChildChild()), equalTo(0L)); 66 | assertThat(deepSizeOf(sizeOf, new ChildChildChild()), equalTo(0L)); 67 | } 68 | 69 | @Test 70 | @SuppressWarnings("unchecked") 71 | public void testCustomAnnotationFiltering() throws Exception { 72 | SizeOf sizeOf = new CrossCheckingSizeOf(new AnnotationSizeOfFilter()); 73 | assertThat(deepSizeOf(sizeOf, new MatchingPatternOrNotAnnotationFilteredField()), allOf(greaterThan(128L), lessThan(16 * 1024L))); 74 | assertThat(deepSizeOf(sizeOf, new MatchingPatternAnnotation()), equalTo(0L)); 75 | assertThat(deepSizeOf(sizeOf, new MatchingPatternAnnotationChild()), equalTo(0L)); 76 | assertThat(deepSizeOf(sizeOf, new MatchingPatternAnnotationNoInheritedChild()), allOf(greaterThan(4L))); 77 | assertThat(deepSizeOf(sizeOf, new NonMatchingPatternAnnotation1()), allOf(greaterThan(4L))); 78 | assertThat(deepSizeOf(sizeOf, new NonMatchingPatternAnnotation2()), allOf(greaterThan(4L))); 79 | assertThat(deepSizeOf(sizeOf, new CustomAnnotationFilteredPackage()), equalTo(0L)); 80 | } 81 | 82 | @Test(expected = IllegalStateException.class) 83 | public void testNotPossibleToHaveTwoIgnoreSizeOfAnnotations() throws Exception { 84 | SizeOf sizeOf = new CrossCheckingSizeOf(new AnnotationSizeOfFilter()); 85 | deepSizeOf(sizeOf, new AnnotatedTwice()); 86 | } 87 | 88 | 89 | public static class AnnotationFilteredField { 90 | 91 | @IgnoreSizeOf 92 | private final byte[] bigArray = new byte[16 * 1024]; 93 | private final byte[] smallArray = new byte[128]; 94 | } 95 | 96 | public static class AnnotationFilteredFieldSubclass extends AnnotationFilteredField { 97 | } 98 | 99 | @IgnoreSizeOf 100 | public static class AnnotationFilteredClass { 101 | 102 | private final byte[] bigArray = new byte[16 * 1024]; 103 | } 104 | 105 | @IgnoreSizeOf(inherited = true) 106 | public static class Parent { 107 | } 108 | 109 | public static class Child extends Parent { 110 | } 111 | 112 | @IgnoreSizeOf 113 | public static class ChildChild extends Child { 114 | } 115 | 116 | public static class ChildChildChild extends ChildChild { 117 | } 118 | 119 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOf 120 | public static class MatchingPatternAnnotation { 121 | } 122 | 123 | public static class MatchingPatternAnnotationChild extends MatchingPatternAnnotation { 124 | } 125 | 126 | @com.terracotta.ehcache.special.annotation.no.inherited.IgnoreSizeOf 127 | public static class MatchingPatternAnnotationNoInherited { 128 | } 129 | 130 | public static class MatchingPatternAnnotationNoInheritedChild extends MatchingPatternAnnotationNoInherited { 131 | } 132 | 133 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOffff 134 | public static class NonMatchingPatternAnnotation1 { 135 | } 136 | 137 | @com.terracotta.special.annotation.IgnoreSizeOf 138 | public static class NonMatchingPatternAnnotation2 { 139 | } 140 | 141 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOf 142 | @IgnoreSizeOf 143 | public static class AnnotatedTwice { 144 | } 145 | 146 | public static class MatchingPatternOrNotAnnotationFilteredField { 147 | @com.terracotta.ehcache.special.annotation.IgnoreSizeOf 148 | private final byte[] matchingBigArray = new byte[16 * 1024]; 149 | @com.terracotta.special.annotation.IgnoreSizeOf 150 | private final byte[] nonMatchingSmallArray = new byte[128]; 151 | } 152 | 153 | public static class ResourceFilteredField { 154 | 155 | private final byte[] bigArray = new byte[16 * 1024]; 156 | private final byte[] smallArray = new byte[128]; 157 | } 158 | 159 | public static class ResourceFilteredFieldSubclass extends ResourceFilteredField { 160 | } 161 | 162 | public static class ResourceFilteredClass { 163 | 164 | private final byte[] bigArray = new byte[6 * 1024]; 165 | } 166 | 167 | public static class Referrer { 168 | 169 | private final Object reference; 170 | 171 | public Referrer(Object obj) { 172 | this.reference = obj; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/ObjectGraphWalkerTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.SizeOfFilter; 20 | import org.ehcache.sizeof.impl.PassThroughFilter; 21 | import org.junit.Test; 22 | 23 | import java.lang.reflect.Field; 24 | import java.util.Collection; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | import java.util.concurrent.locks.ReentrantReadWriteLock; 29 | 30 | import static org.hamcrest.CoreMatchers.is; 31 | import static org.hamcrest.CoreMatchers.sameInstance; 32 | import static org.hamcrest.core.IsNull.nullValue; 33 | import static org.junit.Assert.assertThat; 34 | import static org.junit.Assert.fail; 35 | 36 | /** 37 | * @author Alex Snaps 38 | */ 39 | public class ObjectGraphWalkerTest { 40 | 41 | @Test 42 | public void testWalksAGraph() { 43 | 44 | final Map map = new HashMap<>(); 45 | 46 | ObjectGraphWalker walker = new ObjectGraphWalker( 47 | new ObjectGraphWalker.Visitor() { 48 | public long visit(final Object object) { 49 | increment(object.getClass().getName()); 50 | return 1; 51 | } 52 | 53 | public void increment(String value) { 54 | if (value != null) { 55 | Long previousValue = map.get(value); 56 | if (previousValue == null) { 57 | previousValue = 0L; 58 | } 59 | map.put(value, ++previousValue); 60 | } 61 | } 62 | }, new SizeOfFilter() { 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | public Collection filterFields(Class klazz, Collection fields) { 68 | return fields; 69 | } 70 | 71 | /** 72 | * {@inheritDoc} 73 | */ 74 | public boolean filterClass(Class klazz) { 75 | return true; 76 | } 77 | }, 78 | true); 79 | 80 | assertThat(walker.walk(new ReentrantReadWriteLock()), is(5L)); 81 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter"), is(1L)); 82 | 83 | assertThat(map.remove(ReentrantReadWriteLock.class.getName()), is(1L)); 84 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync"), is(1L)); 85 | assertThat(map.remove(ReentrantReadWriteLock.ReadLock.class.getName()), is(1L)); 86 | assertThat(map.remove(ReentrantReadWriteLock.WriteLock.class.getName()), is(1L)); 87 | assertThat(map.isEmpty(), is(true)); 88 | 89 | assertThat(walker.walk(new SomeInnerClass()), is(14L)); 90 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$Sync$ThreadLocalHoldCounter"), is(1L)); 91 | 92 | assertThat(map.remove(SomeInnerClass.class.getName()), is(1L)); 93 | assertThat(map.remove(this.getClass().getName()), is(1L)); 94 | assertThat(map.remove(Object.class.getName()), is(5L)); 95 | assertThat(map.remove(ReentrantReadWriteLock.class.getName()), is(1L)); 96 | assertThat(map.remove("java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync"), is(1L)); 97 | assertThat(map.remove(ReentrantReadWriteLock.ReadLock.class.getName()), is(1L)); 98 | assertThat(map.remove(ReentrantReadWriteLock.WriteLock.class.getName()), is(1L)); 99 | assertThat(map.remove(Object[].class.getName()), is(1L)); 100 | // auto-boxed '0' is a flyweight - it doesn't get walked 101 | assertThat(map.remove(Integer.class.getName()), nullValue()); 102 | assertThat(map.remove(int[].class.getName()), is(1L)); 103 | assertThat(map.isEmpty(), is(true)); 104 | 105 | assertThat(walker.walk((Object)null), is(0L)); 106 | assertThat(walker.walk(), is(0L)); 107 | } 108 | 109 | public class SomeInnerClass { 110 | 111 | private int value; 112 | private Object one; 113 | private final Object two = new Object(); 114 | private final Object three = new Object(); 115 | private final Object four = new ReentrantReadWriteLock(); 116 | private final Object[] anArray = new Object[] { new Object(), new Object(), new Object(), one, two, two, three, four, value }; 117 | private final int[] anIntArray = new int[] { 1, 2, 1300 }; 118 | 119 | } 120 | 121 | @Test 122 | public void testUsingListenerToLimit() { 123 | 124 | final IllegalStateException illegalArgumentException = new IllegalStateException(); 125 | final int maxDepth = 2; 126 | 127 | final AtomicInteger visited = new AtomicInteger(); 128 | ObjectGraphWalker walker = new ObjectGraphWalker(object -> { 129 | visited.incrementAndGet(); 130 | return -1; 131 | }, new PassThroughFilter(), true); 132 | 133 | final AtomicInteger counter = new AtomicInteger(); 134 | try { 135 | walker.walk((object, size) -> { 136 | if (counter.incrementAndGet() >= maxDepth) { 137 | throw illegalArgumentException; 138 | } 139 | assertThat(size, is(-1L)); 140 | }, new Object(), new Object(), new Object(), new Object()); 141 | fail(); 142 | } catch (IllegalStateException e) { 143 | assertThat(e, sameInstance(illegalArgumentException)); 144 | } 145 | assertThat(visited.get(), is(maxDepth)); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/SizeOfFilterSourceTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.filters.AnnotationSizeOfFilter; 20 | import org.junit.Test; 21 | 22 | import java.io.IOException; 23 | import java.net.URL; 24 | import java.security.SecureClassLoader; 25 | import java.util.Enumeration; 26 | 27 | import static org.hamcrest.CoreMatchers.instanceOf; 28 | import static org.hamcrest.CoreMatchers.is; 29 | import static org.junit.Assert.assertThat; 30 | 31 | /** 32 | * @author Alex Snaps 33 | */ 34 | public class SizeOfFilterSourceTest { 35 | 36 | @Test 37 | public void testContainsAnnotationFilterWhenConfigured() { 38 | SizeOfFilterSource source = new SizeOfFilterSource(true); 39 | assertThat(source.getFilters().length, is(2)); 40 | assertThat(source.getFilters()[1], instanceOf(AnnotationSizeOfFilter.class)); 41 | } 42 | 43 | @Test 44 | public void testStartsEmptyWhenConfigured() { 45 | SizeOfFilterSource source = new SizeOfFilterSource(false); 46 | assertThat(source.getFilters().length, is(1)); 47 | } 48 | 49 | @Test 50 | public void testAppliesMutators() { 51 | SizeOfFilterSource source = new SizeOfFilterSource(false); 52 | assertThat(source.getFilters().length, is(1)); 53 | source.applyMutators(new CheatingClassLoader()); 54 | assertThat(source.getFilters().length, is(1)); 55 | assertThat(source.getFilters()[0].filterClass(TestMutator.class), is(false)); 56 | assertThat(source.getFilters()[0].filterClass(String.class), is(true)); 57 | } 58 | 59 | private static class CheatingClassLoader extends SecureClassLoader { 60 | 61 | public CheatingClassLoader() { 62 | super(SizeOfFilterSourceTest.class.getClassLoader()); 63 | } 64 | 65 | @Override 66 | public Enumeration getResources(final String name) throws IOException { 67 | final String className = FilterConfigurator.class.getName(); 68 | if (name.equals("META-INF/services/" + className)) { 69 | return super.getResources("services/" + className + ".txt"); 70 | } 71 | return super.getResources(name); 72 | } 73 | } 74 | 75 | public static final class TestMutator implements FilterConfigurator { 76 | 77 | @Override 78 | public void configure(final Filter ehcacheFilter) { 79 | ehcacheFilter.ignoreInstancesOf(TestMutator.class, true); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/SizeOfTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.impl.JvmInformation; 20 | import org.junit.Assert; 21 | import org.junit.Assume; 22 | import org.junit.BeforeClass; 23 | import org.junit.Test; 24 | 25 | import java.math.BigDecimal; 26 | import java.math.BigInteger; 27 | import java.math.MathContext; 28 | import java.net.Proxy; 29 | import java.nio.charset.CodingErrorAction; 30 | import java.util.ArrayList; 31 | import java.util.Collection; 32 | import java.util.Collections; 33 | import java.util.LinkedList; 34 | import java.util.List; 35 | import java.util.Locale; 36 | import java.util.concurrent.TimeUnit; 37 | import java.util.concurrent.atomic.AtomicLong; 38 | import java.util.concurrent.locks.ReentrantReadWriteLock; 39 | import java.util.logging.Logger; 40 | 41 | import javax.xml.datatype.DatatypeConstants; 42 | 43 | import static org.ehcache.sizeof.impl.JvmInformation.CURRENT_JVM_INFORMATION; 44 | import static org.ehcache.sizeof.impl.JvmInformation.UNKNOWN_32_BIT; 45 | import static org.ehcache.sizeof.impl.JvmInformation.UNKNOWN_64_BIT; 46 | import static org.hamcrest.CoreMatchers.is; 47 | import static org.hamcrest.core.IsNot.not; 48 | import static org.hamcrest.core.StringContains.containsString; 49 | import static org.junit.Assert.assertTrue; 50 | import static org.junit.Assume.assumeThat; 51 | 52 | /** 53 | * @author Alex Snaps 54 | */ 55 | public class SizeOfTest extends AbstractSizeOfTest { 56 | public Object[] container; 57 | 58 | private static long deepSizeOf(SizeOf sizeOf, Object... obj) { 59 | return sizeOf.deepSizeOf(obj); 60 | } 61 | 62 | @BeforeClass 63 | public static void setup() { 64 | System.err.println("java.vm.name:\t" + System.getProperty("java.vm.name", "")); 65 | System.err.println("java.vm.vendor:\t" + System.getProperty("java.vm.vendor", "")); 66 | assumeThat(System.getProperty("os.name"), not(containsString("AIX"))); 67 | deepSizeOf(new CrossCheckingSizeOf(), (Object) null); 68 | System.err.println("JVM identified as: " + JvmInformation.CURRENT_JVM_INFORMATION); 69 | if (JvmInformation.CURRENT_JVM_INFORMATION == UNKNOWN_64_BIT || JvmInformation.CURRENT_JVM_INFORMATION == UNKNOWN_32_BIT) { 70 | System.getProperties().list(System.err); 71 | } 72 | } 73 | 74 | private final Collection sizeOfFailures = new LinkedList<>(); 75 | 76 | @Test 77 | public void testCreatesSizeOfEngine() { 78 | assertTrue(SizeOf.newInstance().sizeOf(new Object()) > 0); 79 | } 80 | 81 | @Test 82 | public void testSizeOfFlyweight() throws Exception { 83 | SizeOf sizeOf = new CrossCheckingSizeOf(false); 84 | Assert.assertThat(deepSizeOf(sizeOf, 42), is(not(0L))); 85 | } 86 | 87 | @Test 88 | public void testSizeOf() throws Exception { 89 | Assume.assumeThat(CURRENT_JVM_INFORMATION.getMinimumObjectSize(), is(CURRENT_JVM_INFORMATION.getObjectAlignment())); 90 | 91 | SizeOf sizeOf = new CrossCheckingSizeOf(); 92 | Assert.assertThat(deepSizeOf(sizeOf, TimeUnit.SECONDS), is(0L)); 93 | Assert.assertThat(deepSizeOf(sizeOf, Object.class), is(0L)); 94 | Assert.assertThat(deepSizeOf(sizeOf, 1), is(0L)); 95 | Assert.assertThat(deepSizeOf(sizeOf, BigInteger.ZERO), is(0L)); 96 | Assert.assertThat(deepSizeOf(sizeOf, BigDecimal.ZERO), is(0L)); 97 | Assert.assertThat(deepSizeOf(sizeOf, MathContext.UNLIMITED), is(0L)); 98 | Assert.assertThat(deepSizeOf(sizeOf, Locale.ENGLISH), is(0L)); 99 | Assert.assertThat(deepSizeOf(sizeOf, Logger.getGlobal()), is(0L)); 100 | Assert.assertThat(deepSizeOf(sizeOf, Collections.EMPTY_SET), is(0L)); 101 | Assert.assertThat(deepSizeOf(sizeOf, Collections.EMPTY_LIST), is(0L)); 102 | Assert.assertThat(deepSizeOf(sizeOf, Collections.EMPTY_MAP), is(0L)); 103 | Assert.assertThat(deepSizeOf(sizeOf, String.CASE_INSENSITIVE_ORDER), is(0L)); 104 | Assert.assertThat(deepSizeOf(sizeOf, System.err), is(0L)); 105 | Assert.assertThat(deepSizeOf(sizeOf, Proxy.NO_PROXY), is(0L)); 106 | Assert.assertThat(deepSizeOf(sizeOf, CodingErrorAction.REPORT), is(0L)); 107 | Assert.assertThat(deepSizeOf(sizeOf, DatatypeConstants.DAYS), is(0L)); 108 | Assert.assertThat(deepSizeOf(sizeOf, DatatypeConstants.TIME), is(0L)); 109 | 110 | assertThat(sizeOf.sizeOf(new Object()), "sizeOf(new Object())"); 111 | assertThat(sizeOf.sizeOf(1), "sizeOf(new Integer(1))"); 112 | assertThat(sizeOf.sizeOf(1000), "sizeOf(1000)"); 113 | assertThat(deepSizeOf(sizeOf, new SomeClass(false)), "deepSizeOf(new SomeClass(false))"); 114 | assertThat(deepSizeOf(sizeOf, new SomeClass(true)), "deepSizeOf(new SomeClass(true))"); 115 | assertThat(sizeOf.sizeOf(new Object[] { }), "sizeOf(new Object[] { })"); 116 | assertThat(sizeOf.sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() }), "sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })"); 117 | assertThat(sizeOf.sizeOf(new int[] { }), "sizeOf(new int[] { })"); 118 | assertThat(sizeOf.sizeOf(new int[] { 987654, 876543, 765432, 654321 }), "sizeOf(new int[] { 987654, 876543, 765432, 654321 })"); 119 | assertThat(deepSizeOf(sizeOf, new Pair(null, null)), "deepSizeOf(new Pair(null, null))"); 120 | assertThat(deepSizeOf(sizeOf, new Pair(new Object(), null)), "deepSizeOf(new Pair(new Object(), null))"); 121 | assertThat(deepSizeOf(sizeOf, new Pair(new Object(), new Object())), "deepSizeOf(new Pair(new Object(), new Object()))"); 122 | assertThat(deepSizeOf(sizeOf, new ReentrantReadWriteLock()), "deepSizeOf(new ReentrantReadWriteLock())"); 123 | 124 | if (!sizeOfFailures.isEmpty()) { 125 | StringBuilder sb = new StringBuilder(); 126 | for (AssertionError e : sizeOfFailures) { 127 | sb.append(e.toString()).append('\n'); 128 | } 129 | Assert.fail(sb.toString()); 130 | } 131 | 132 | List list1 = new ArrayList<>(); 133 | List list2 = new ArrayList<>(); 134 | 135 | Object someInstance = new Object(); 136 | list1.add(someInstance); 137 | list2.add(someInstance); 138 | 139 | Assert.assertThat(deepSizeOf(sizeOf, list1), is(deepSizeOf(sizeOf, list2))); 140 | Assert.assertThat(deepSizeOf(sizeOf, list1, list2) < (deepSizeOf(sizeOf, list1) + deepSizeOf(sizeOf, list2)), is(true)); 141 | list2.add(new Object()); 142 | Assert.assertThat(deepSizeOf(sizeOf, list2) > deepSizeOf(sizeOf, list1), is(true)); 143 | } 144 | 145 | private void assertThat(Long size, String expression) { 146 | try { 147 | Assert.assertThat(expression, size, is(SizeOfTestValues.get(expression))); 148 | } catch (AssertionError e) { 149 | sizeOfFailures.add(e); 150 | } 151 | } 152 | 153 | @Test 154 | public void testOnHeapConsumption() throws Exception { 155 | SizeOf sizeOf = new CrossCheckingSizeOf(); 156 | 157 | int size = 80000; 158 | int perfectMatches = 0; 159 | for (int j = 0; j < 5; j++) { 160 | container = new Object[size]; 161 | long usedBefore = measureMemoryUse(); 162 | for (int i = 0; i < size; i++) { 163 | container[i] = new EvilPair(new Object(), new SomeClass(i % 2 == 0)); 164 | } 165 | 166 | long mem = 0; 167 | for (Object s : container) { 168 | mem += deepSizeOf(sizeOf, s); 169 | } 170 | 171 | long used = measureMemoryUse() - usedBefore; 172 | float percentage = 1 - (mem / (float)used); 173 | System.err.println("Run # " + (j + 1) + ": Deviation of " + String.format("%.3f", percentage * -100) + 174 | "%\n" + used + 175 | " bytes are actually being used, while we believe " + mem + " are"); 176 | if (j > 1) { 177 | Assert.assertThat("Run # " + (j + 1) + ": Deviation of " + String.format("%.3f", percentage * -100) + 178 | "% was above the +/-1.5% delta threshold \n" + used + 179 | " bytes are actually being used, while we believe " + mem + " are (" + 180 | (used - mem) / size + ")", 181 | Math.abs(percentage) < .015f, is(true)); 182 | } 183 | if (used == mem && ++perfectMatches > 1) { 184 | System.err.println("Two perfect matches, that's good enough... bye y'all!"); 185 | break; 186 | } 187 | } 188 | } 189 | 190 | private long measureMemoryUse() throws InterruptedException { 191 | long total; 192 | long freeAfter; 193 | long freeBefore; 194 | Runtime runtime = Runtime.getRuntime(); 195 | do { 196 | total = runtime.totalMemory(); 197 | freeBefore = runtime.freeMemory(); 198 | System.gc(); 199 | Thread.sleep(100); 200 | freeAfter = runtime.freeMemory(); 201 | } while (total != runtime.totalMemory() || freeAfter > freeBefore); 202 | return total - freeAfter; 203 | } 204 | 205 | public static class SomeClass { 206 | 207 | public Object ref; 208 | 209 | public SomeClass(final boolean init) { 210 | if (init) { 211 | ref = new Object(); 212 | } 213 | } 214 | } 215 | 216 | public static class Pair { 217 | private final Object one; 218 | private final Object two; 219 | 220 | public Pair(final Object one, final Object two) { 221 | this.one = one; 222 | this.two = two; 223 | } 224 | } 225 | 226 | public static final class Stupid { 227 | 228 | public static class internal { 229 | private int someValue; 230 | private long otherValue; 231 | } 232 | 233 | internal internalVar = new internal(); 234 | int someValue; 235 | long someOther; 236 | long otherValue; 237 | boolean bool; 238 | } 239 | 240 | public static final class EvilPair extends Pair { 241 | 242 | private static final AtomicLong counter = new AtomicLong(Long.MIN_VALUE); 243 | 244 | private final Object oneHidden; 245 | private final Object twoHidden; 246 | private final Object copyOne; 247 | private final Object copyTwo; 248 | private final long instanceNumber; 249 | 250 | private EvilPair(final Object one, final Object two) { 251 | super(one, two); 252 | instanceNumber = counter.getAndIncrement(); 253 | if (instanceNumber % 4 == 0) { 254 | oneHidden = new Object(); 255 | twoHidden = new Object(); 256 | } else { 257 | oneHidden = null; 258 | twoHidden = null; 259 | } 260 | this.copyOne = one; 261 | this.copyTwo = two; 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/SizeOfTestValues.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof; 18 | 19 | import org.ehcache.sizeof.impl.JvmInformation; 20 | import org.junit.Assert; 21 | 22 | import java.util.Collections; 23 | import java.util.EnumMap; 24 | import java.util.HashMap; 25 | import java.util.Map; 26 | 27 | import static org.hamcrest.Matchers.hasKey; 28 | 29 | /** 30 | * @author Alex Snaps 31 | */ 32 | public class SizeOfTestValues { 33 | private static final Map> CORRECT_SIZES = new EnumMap<>(JvmInformation.class); 34 | 35 | static { 36 | Map hotspot32Bit = new HashMap<>(); 37 | hotspot32Bit.put("sizeOf(new Object())", 8L); 38 | hotspot32Bit.put("sizeOf(new Integer(1))", 16L); 39 | hotspot32Bit.put("sizeOf(1000)", 16L); 40 | hotspot32Bit.put("deepSizeOf(new SomeClass(false))", 16L); 41 | hotspot32Bit.put("deepSizeOf(new SomeClass(true))", 24L); 42 | hotspot32Bit.put("sizeOf(new Object[] { })", 16L); 43 | hotspot32Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L); 44 | hotspot32Bit.put("sizeOf(new int[] { })", 16L); 45 | hotspot32Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L); 46 | hotspot32Bit.put("deepSizeOf(new Pair(null, null))", 16L); 47 | hotspot32Bit.put("deepSizeOf(new Pair(new Object(), null))", 24L); 48 | hotspot32Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 32L); 49 | hotspot32Bit.put("deepSizeOf(new ReentrantReadWriteLock())", 112L); 50 | 51 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_32_BIT, hotspot32Bit); 52 | CORRECT_SIZES.put(JvmInformation.OPENJDK_32_BIT, hotspot32Bit); 53 | 54 | Map hotspot32BitWithConcurrentMarkAndSweep = new HashMap<>(); 55 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Object())", 16L); 56 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Integer(1))", 16L); 57 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(1000)", 16L); 58 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new SomeClass(false))", 16L); 59 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new SomeClass(true))", 24L); 60 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Object[] { })", 16L); 61 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L); 62 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new int[] { })", 16L); 63 | hotspot32BitWithConcurrentMarkAndSweep.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L); 64 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new Pair(null, null))", 16L); 65 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new Pair(new Object(), null))", 24L); 66 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new Pair(new Object(), new Object()))", 32L); 67 | hotspot32BitWithConcurrentMarkAndSweep.put("deepSizeOf(new ReentrantReadWriteLock())", 112L); 68 | 69 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot32BitWithConcurrentMarkAndSweep); 70 | CORRECT_SIZES.put(JvmInformation.OPENJDK_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot32BitWithConcurrentMarkAndSweep); 71 | 72 | Map hotspot64Bit = new HashMap<>(); 73 | hotspot64Bit.put("sizeOf(new Object())", 16L); 74 | hotspot64Bit.put("sizeOf(new Integer(1))", 24L); 75 | hotspot64Bit.put("sizeOf(1000)", 24L); 76 | hotspot64Bit.put("deepSizeOf(new SomeClass(false))", 24L); 77 | hotspot64Bit.put("deepSizeOf(new SomeClass(true))", 40L); 78 | hotspot64Bit.put("sizeOf(new Object[] { })", 24L); 79 | hotspot64Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 56L); 80 | hotspot64Bit.put("sizeOf(new int[] { })", 24L); 81 | hotspot64Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 40L); 82 | hotspot64Bit.put("deepSizeOf(new Pair(null, null))", 32L); 83 | hotspot64Bit.put("deepSizeOf(new Pair(new Object(), null))", 48L); 84 | hotspot64Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 64L); 85 | hotspot64Bit.put("deepSizeOf(new ReentrantReadWriteLock())", 192L); 86 | 87 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT, hotspot64Bit); 88 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT, hotspot64Bit); 89 | 90 | Map hotspot64BitWithCMS = new HashMap<>(); 91 | hotspot64BitWithCMS.put("sizeOf(new Object())", 24L); 92 | hotspot64BitWithCMS.put("sizeOf(new Integer(1))", 24L); 93 | hotspot64BitWithCMS.put("sizeOf(1000)", 24L); 94 | hotspot64BitWithCMS.put("deepSizeOf(new SomeClass(false))", 24L); 95 | hotspot64BitWithCMS.put("deepSizeOf(new SomeClass(true))", 40L); 96 | hotspot64BitWithCMS.put("sizeOf(new Object[] { })", 24L); 97 | hotspot64BitWithCMS.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 40L); 98 | hotspot64BitWithCMS.put("sizeOf(new int[] { })", 24L); 99 | hotspot64BitWithCMS.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 40L); 100 | hotspot64BitWithCMS.put("deepSizeOf(new Pair(null, null))", 32L); 101 | hotspot64BitWithCMS.put("deepSizeOf(new Pair(new Object(), null))", 48L); 102 | hotspot64BitWithCMS.put("deepSizeOf(new Pair(new Object(), new Object()))", 64L); 103 | hotspot64BitWithCMS.put("deepSizeOf(new ReentrantReadWriteLock())", 192L); 104 | 105 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCMS); 106 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCMS); 107 | 108 | Map hotspot64BitWithCompressedOops = new HashMap<>(); 109 | hotspot64BitWithCompressedOops.put("sizeOf(new Object())", 16L); 110 | hotspot64BitWithCompressedOops.put("sizeOf(new Integer(1))", 16L); 111 | hotspot64BitWithCompressedOops.put("sizeOf(1000)", 16L); 112 | hotspot64BitWithCompressedOops.put("deepSizeOf(new SomeClass(false))", 16L); 113 | hotspot64BitWithCompressedOops.put("deepSizeOf(new SomeClass(true))", 32L); 114 | hotspot64BitWithCompressedOops.put("sizeOf(new Object[] { })", 16L); 115 | hotspot64BitWithCompressedOops.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L); 116 | hotspot64BitWithCompressedOops.put("sizeOf(new int[] { })", 16L); 117 | hotspot64BitWithCompressedOops.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L); 118 | hotspot64BitWithCompressedOops.put("deepSizeOf(new Pair(null, null))", 24L); 119 | hotspot64BitWithCompressedOops.put("deepSizeOf(new Pair(new Object(), null))", 40L); 120 | hotspot64BitWithCompressedOops.put("deepSizeOf(new Pair(new Object(), new Object()))", 56L); 121 | hotspot64BitWithCompressedOops.put("deepSizeOf(new ReentrantReadWriteLock())", 120L); 122 | 123 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS, hotspot64BitWithCompressedOops); 124 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS, hotspot64BitWithCompressedOops); 125 | 126 | Map hotspot64BitWithCompressedOopsAndCMS = new HashMap<>(); 127 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Object())", 24L); 128 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Integer(1))", 24L); 129 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(1000)", 24L); 130 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new SomeClass(false))", 24L); 131 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new SomeClass(true))", 32L); 132 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Object[] { })", 24L); 133 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L); 134 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new int[] { })", 24L); 135 | hotspot64BitWithCompressedOopsAndCMS.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L); 136 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new Pair(null, null))", 24L); 137 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new Pair(new Object(), null))", 40L); 138 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new Pair(new Object(), new Object()))", 56L); 139 | hotspot64BitWithCompressedOopsAndCMS.put("deepSizeOf(new ReentrantReadWriteLock())", 120L); 140 | 141 | CORRECT_SIZES.put(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCompressedOopsAndCMS); 142 | CORRECT_SIZES.put(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, hotspot64BitWithCompressedOopsAndCMS); 143 | 144 | Map ibm32Bit = new HashMap<>(); 145 | ibm32Bit.put("sizeOf(new Object())", 16L); 146 | ibm32Bit.put("sizeOf(new Integer(1))", 16L); 147 | ibm32Bit.put("sizeOf(1000)", 16L); 148 | ibm32Bit.put("deepSizeOf(new SomeClass(false))", 16L); 149 | ibm32Bit.put("deepSizeOf(new SomeClass(true))", 32L); 150 | ibm32Bit.put("sizeOf(new Object[] { })", 16L); 151 | ibm32Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 32L); 152 | ibm32Bit.put("sizeOf(new int[] { })", 16L); 153 | ibm32Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 32L); 154 | ibm32Bit.put("deepSizeOf(new Pair(null, null))", 24L); 155 | ibm32Bit.put("deepSizeOf(new Pair(new Object(), null))", 40L); 156 | ibm32Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 56L); 157 | 158 | CORRECT_SIZES.put(JvmInformation.IBM_32_BIT, ibm32Bit); 159 | 160 | Map ibm64Bit = new HashMap<>(); 161 | ibm64Bit.put("sizeOf(new Object())", 24L); 162 | ibm64Bit.put("sizeOf(new Integer(1))", 32L); 163 | ibm64Bit.put("sizeOf(1000)", 32L); 164 | ibm64Bit.put("deepSizeOf(new SomeClass(false))", 32L); 165 | ibm64Bit.put("deepSizeOf(new SomeClass(true))", 56L); 166 | ibm64Bit.put("sizeOf(new Object[] { })", 24L); 167 | ibm64Bit.put("sizeOf(new Object[] { new Object(), new Object(), new Object(), new Object() })", 56L); 168 | ibm64Bit.put("sizeOf(new int[] { })", 24L); 169 | ibm64Bit.put("sizeOf(new int[] { 987654, 876543, 765432, 654321 })", 40L); 170 | ibm64Bit.put("deepSizeOf(new Pair(null, null))", 40L); 171 | ibm64Bit.put("deepSizeOf(new Pair(new Object(), null))", 64L); 172 | ibm64Bit.put("deepSizeOf(new Pair(new Object(), new Object()))", 88L); 173 | 174 | CORRECT_SIZES.put(JvmInformation.IBM_64_BIT, ibm64Bit); 175 | 176 | CORRECT_SIZES.put(JvmInformation.IBM_64_BIT_WITH_COMPRESSED_REFS, Collections.emptyMap()); 177 | 178 | CORRECT_SIZES.put(JvmInformation.UNKNOWN_32_BIT, Collections.emptyMap()); 179 | CORRECT_SIZES.put(JvmInformation.UNKNOWN_64_BIT, Collections.emptyMap()); 180 | 181 | for (JvmInformation jvm : JvmInformation.values()) { 182 | Assert.assertThat(CORRECT_SIZES, hasKey(jvm)); 183 | } 184 | } 185 | 186 | public static Long get(String expression) { 187 | return CORRECT_SIZES.get(JvmInformation.CURRENT_JVM_INFORMATION).get(expression); 188 | } 189 | 190 | private SizeOfTestValues() { 191 | //not instantiable 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/annotations/AnnotationProxyFactoryTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.annotations; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | 23 | /** 24 | * @author Alex Snaps 25 | */ 26 | public class AnnotationProxyFactoryTest { 27 | 28 | @Test(expected = UnsupportedOperationException.class) 29 | public void NoDefaultValueInReferenceAnnotationAndNoImplementationInCustomThrowsException_Test() { 30 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class); 31 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory 32 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class); 33 | annotationProxy.things(); 34 | } 35 | 36 | @Test 37 | public void NoImplementationInCustomFallsBackToReferenceImplementation_Test() { 38 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class); 39 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory 40 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class); 41 | assertEquals("hello", annotationProxy.version()); 42 | } 43 | 44 | 45 | @Test 46 | public void IfMethodExistsInCustomAnnotationRedirectsToIt_Test() { 47 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class); 48 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory 49 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class); 50 | assertEquals(true, annotationProxy.deprecated()); 51 | } 52 | 53 | @Test 54 | public void IfMethodExistsInCustomAnnotationButReturnTypeIsDifferentFallsBackToReferenceImplementation_Test() { 55 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class); 56 | ReferenceAnnotation annotationProxy = AnnotationProxyFactory 57 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class); 58 | assertEquals(5, annotationProxy.differentReturnType()); 59 | } 60 | 61 | @Test 62 | public void DifferentReturnTypesFromDifferentMethods_Test() { 63 | CustomAnnotation customAnnotation = UsingCustomAnnotation.class.getAnnotation(CustomAnnotation.class); 64 | ReferenceAnnotation annotationProxy = org 65 | .ehcache 66 | .sizeof 67 | .annotations 68 | .AnnotationProxyFactory 69 | .getAnnotationProxy(customAnnotation, ReferenceAnnotation.class); 70 | assertEquals(Integer.class, annotationProxy.aClass()); 71 | assertEquals(ExampleEnum.TWO, annotationProxy.anEnum()); 72 | assertEquals(customAnnotation.anAnnotation(), annotationProxy.anAnnotation()); 73 | } 74 | 75 | 76 | @CustomAnnotation 77 | class UsingCustomAnnotation { 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/annotations/CustomAnnotation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.annotations; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE }) 26 | public @interface CustomAnnotation { 27 | boolean deprecated() default true; 28 | 29 | short differentReturnType() default 10; 30 | 31 | Class aClass() default Integer.class; 32 | 33 | ExampleEnum anEnum() default ExampleEnum.TWO; 34 | 35 | Deprecated anAnnotation() default @Deprecated; 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/annotations/ExampleEnum.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.annotations; 18 | 19 | /** 20 | * @author Alex Snaps 21 | */ 22 | public enum ExampleEnum { 23 | ONE, TWO 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/annotations/ReferenceAnnotation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.annotations; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.PACKAGE }) 26 | public @interface ReferenceAnnotation { 27 | String version() default "hello"; 28 | 29 | boolean deprecated() default false; 30 | 31 | int differentReturnType() default 5; 32 | 33 | String[] things(); 34 | 35 | Class aClass() default String.class; 36 | 37 | ExampleEnum anEnum() default ExampleEnum.ONE; 38 | 39 | Deprecated anAnnotation() default @Deprecated; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/filteredtest/AnnotationFilteredPackage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.filteredtest; 18 | 19 | /** 20 | * @author Alex Snaps 21 | */ 22 | public class AnnotationFilteredPackage { 23 | 24 | private final byte[] bigArray = new byte[16 * 1024]; 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/filteredtest/custom/CustomAnnotationFilteredPackage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.filteredtest.custom; 18 | 19 | /** 20 | * @author Alex Snaps 21 | */ 22 | public class CustomAnnotationFilteredPackage { 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/filteredtest/custom/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | @IgnoreSizeOf package org.ehcache.sizeof.filteredtest.custom; 18 | 19 | import com.terracotta.ehcache.special.annotation.IgnoreSizeOf; 20 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/filteredtest/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | @IgnoreSizeOf package org.ehcache.sizeof.filteredtest; 18 | 19 | import org.ehcache.sizeof.annotations.IgnoreSizeOf; -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/filters/TypeFilterTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.filters; 18 | 19 | import org.junit.Test; 20 | 21 | import java.lang.reflect.Field; 22 | import java.util.Collection; 23 | import java.util.Collections; 24 | import java.util.HashSet; 25 | 26 | import static org.hamcrest.CoreMatchers.is; 27 | import static org.junit.Assert.assertThat; 28 | 29 | /** 30 | * @author Alex Snaps 31 | */ 32 | public class TypeFilterTest { 33 | 34 | @Test 35 | public void testStoresClassesToFilter() { 36 | TypeFilter filter = new TypeFilter(); 37 | final Class klazz = String.class; 38 | assertThat(filter.filterClass(klazz), is(true)); 39 | filter.addClass(klazz, true); 40 | assertThat(filter.filterClass(klazz), is(false)); 41 | } 42 | 43 | @Test 44 | public void testStoresFieldsToFilter() throws NoSuchFieldException { 45 | TypeFilter filter = new TypeFilter(); 46 | final Field field = String.class.getDeclaredField("value"); 47 | final HashSet fields = new HashSet<>(); 48 | Collections.addAll(fields, String.class.getDeclaredFields()); 49 | assertThat(fields.contains(field), is(true)); 50 | filter.addField(field); 51 | final Collection filtered = filter.filterFields(String.class, fields); 52 | assertThat(filtered.contains(field), is(false)); 53 | assertThat(filtered.isEmpty(), is(false)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/impl/AgentLoaderRaceTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Assume; 21 | import org.junit.BeforeClass; 22 | import org.junit.Test; 23 | 24 | import java.net.URL; 25 | import java.net.URLClassLoader; 26 | import java.util.Arrays; 27 | import java.util.List; 28 | import java.util.concurrent.Callable; 29 | import java.util.concurrent.ExecutionException; 30 | import java.util.concurrent.ExecutorService; 31 | import java.util.concurrent.Executors; 32 | import java.util.concurrent.Future; 33 | 34 | import static org.hamcrest.core.AnyOf.anyOf; 35 | import static org.hamcrest.core.IsNot.not; 36 | import static org.hamcrest.core.IsNull.nullValue; 37 | import static org.hamcrest.core.StringStartsWith.startsWith; 38 | 39 | /** 40 | * @author Alex Snaps 41 | */ 42 | public class AgentLoaderRaceTest { 43 | 44 | /* 45 | * This test tries to expose an agent loading race seen in MNK-3255. 46 | * 47 | * To trigger a failure the locking in AgentLoader.loadAgent has to be removed 48 | * and a sleep can be added after the check but before the load to open the 49 | * race wider. 50 | */ 51 | @Test 52 | public void testAgentLoaderRace() throws InterruptedException, ExecutionException { 53 | final URL[] urls = ((URLClassLoader)AgentSizeOf.class.getClassLoader()).getURLs(); 54 | 55 | Callable agentLoader1 = new Loader(new URLClassLoader(urls, null)); 56 | Callable agentLoader2 = new Loader(new URLClassLoader(urls, null)); 57 | 58 | ExecutorService executor = Executors.newFixedThreadPool(2); 59 | List> results = executor.invokeAll(Arrays.asList(agentLoader1, agentLoader2)); 60 | 61 | 62 | for (Future f : results) { 63 | Assert.assertThat(f.get(), nullValue()); 64 | } 65 | } 66 | 67 | static class Loader implements Callable { 68 | 69 | private final ClassLoader loader; 70 | 71 | Loader(ClassLoader loader) { 72 | this.loader = loader; 73 | } 74 | 75 | public Throwable call() { 76 | try { 77 | loader.loadClass(AgentSizeOf.class.getName()).newInstance(); 78 | return null; 79 | } catch (Throwable t) { 80 | return t; 81 | } 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/impl/AgentLoaderSystemPropTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import org.junit.After; 20 | import org.junit.Test; 21 | 22 | import static org.hamcrest.CoreMatchers.notNullValue; 23 | import static org.hamcrest.Matchers.is; 24 | import static org.junit.Assert.assertThat; 25 | 26 | /** 27 | * @author Alex Snaps 28 | */ 29 | public class AgentLoaderSystemPropTest { 30 | 31 | @After 32 | public void after() { 33 | System.getProperties().remove("org.ehcache.sizeof.agent.instrumentationSystemProperty"); 34 | System.getProperties().remove(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME); 35 | } 36 | 37 | @Test 38 | public void testLoadsAgentIntoSystemPropsWhenRequired() { 39 | System.setProperty("org.ehcache.sizeof.agent.instrumentationSystemProperty", "true"); 40 | AgentLoader.loadAgent(); 41 | assertThat(AgentLoader.agentIsAvailable(), is(true)); 42 | assertThat(System.getProperties().get(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME), notNullValue()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/impl/AgentLoaderTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import org.junit.After; 20 | import org.junit.Test; 21 | 22 | import static org.hamcrest.CoreMatchers.is; 23 | import static org.hamcrest.CoreMatchers.nullValue; 24 | import static org.junit.Assert.assertThat; 25 | 26 | /** 27 | * @author Alex Snaps 28 | */ 29 | public class AgentLoaderTest { 30 | 31 | @After 32 | public void after() { 33 | System.getProperties().remove("org.ehcache.sizeof.agent.instrumentationSystemProperty"); 34 | System.getProperties().remove(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME); 35 | } 36 | 37 | @Test 38 | public void testLoadsAgentProperly() { 39 | assertThat(Boolean.getBoolean("org.ehcache.sizeof.agent.instrumentationSystemProperty"), is(false)); 40 | AgentLoader.loadAgent(); 41 | assertThat(AgentLoader.agentIsAvailable(), is(true)); 42 | assertThat(System.getProperties().get(AgentLoader.INSTRUMENTATION_INSTANCE_SYSTEM_PROPERTY_NAME), nullValue()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/ehcache/sizeof/impl/JvmInformationTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright Terracotta, Inc. 3 | * Copyright IBM Corp. 2024, 2025 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.ehcache.sizeof.impl; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.hamcrest.CoreMatchers.is; 22 | import static org.junit.Assert.assertThat; 23 | 24 | /** 25 | * Basic test used during refactoring of enum to make sure no differences were introduced 26 | */ 27 | public class JvmInformationTest { 28 | 29 | @Test 30 | public void hotspot32Bits() { 31 | verifyJvmInfo(JvmInformation.HOTSPOT_32_BIT, 0, 0, 4, 8, 8, 8, 4, true, true, true); 32 | verifyJvmInfo(JvmInformation.HOTSPOT_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 16, 8, 8, 4, true, true, true); 33 | } 34 | 35 | @Test 36 | public void hotspot64Bits() { 37 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT, 0, 0, 8, 8, 8, 16, 8, true, true, true); 38 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 8, 24, 8, 16, 8, true, true, true); 39 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS, 0, 0, 4, 8, 8, 12, 8, true, true, true); 40 | verifyJvmInfo(JvmInformation.HOTSPOT_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 24, 8, 12, 8, true, true, true); 41 | } 42 | 43 | @Test 44 | public void openJdk32Bits() { 45 | verifyJvmInfo(JvmInformation.OPENJDK_32_BIT, 0, 0, 4, 8, 8, 8, 4, true, true, true); 46 | verifyJvmInfo(JvmInformation.OPENJDK_32_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 16, 8, 8, 4, true, true, true); 47 | } 48 | 49 | @Test 50 | public void openJdk64Bits() { 51 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT, 0, 0, 8, 8, 8, 16, 8, true, true, true); 52 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT_WITH_CONCURRENT_MARK_AND_SWEEP, 0, 0, 8, 24, 8, 16, 8, true, true, true); 53 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS, 0, 0, 4, 8, 8, 12, 8, true, true, true); 54 | verifyJvmInfo(JvmInformation.OPENJDK_64_BIT_WITH_COMPRESSED_OOPS_AND_CONCURRENT_MARK_AND_SWEEP, 0, 0, 4, 24, 8, 12, 8, true, true, true); 55 | } 56 | 57 | @Test 58 | public void ibm32Bits() { 59 | verifyJvmInfo(JvmInformation.IBM_32_BIT, 0, 0, 4, 8, 8, 16, 4, true, false, true); 60 | } 61 | 62 | @Test 63 | public void ibm64Bits() { 64 | verifyJvmInfo(JvmInformation.IBM_64_BIT, 0, 0, 8, 8, 8, 24, 8, true, false, true); 65 | verifyJvmInfo(JvmInformation.IBM_64_BIT_WITH_COMPRESSED_REFS, 0, 0, 4, 8, 8, 16, 4, true, false, true); 66 | } 67 | 68 | @Test 69 | public void unknown() { 70 | verifyJvmInfo(JvmInformation.UNKNOWN_32_BIT, 0, 0, 4, 8, 8, 8, 4, true, true, true); 71 | verifyJvmInfo(JvmInformation.UNKNOWN_64_BIT, 0, 0, 8, 8, 8, 16, 8, true, true, true); 72 | } 73 | 74 | private void verifyJvmInfo(JvmInformation jvmInfo, int agentSizeOfAdj, int fieldOffsetAdj, int javaPointerSize, int minObjSize, int objAlign, int objHeaderSize, int pointerSize, 75 | boolean supportAgentSizeOf, boolean supportReflectionSizeOf, boolean supportUnsafeSizeOf) { 76 | assertThat(jvmInfo.getAgentSizeOfAdjustment(), is(agentSizeOfAdj)); 77 | assertThat(jvmInfo.getFieldOffsetAdjustment(), is(fieldOffsetAdj)); 78 | assertThat(jvmInfo.getJavaPointerSize(), is(javaPointerSize)); 79 | assertThat(jvmInfo.getMinimumObjectSize(), is(minObjSize)); 80 | assertThat(jvmInfo.getObjectAlignment(), is(objAlign)); 81 | assertThat(jvmInfo.getObjectHeaderSize(), is(objHeaderSize)); 82 | assertThat(jvmInfo.getPointerSize(), is(pointerSize)); 83 | assertThat(jvmInfo.supportsAgentSizeOf(), is(supportAgentSizeOf)); 84 | assertThat(jvmInfo.supportsReflectionSizeOf(), is(supportReflectionSizeOf)); 85 | assertThat(jvmInfo.supportsUnsafeSizeOf(), is(supportUnsafeSizeOf)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/test/resources/services/org.ehcache.sizeof.FilterConfigurator.txt: -------------------------------------------------------------------------------- 1 | org.ehcache.sizeof.SizeOfFilterSourceTest$TestMutator --------------------------------------------------------------------------------