/*
 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.svm.core.util;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.graalvm.compiler.core.common.SuppressFBWarnings;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.jdk.StringInternSupport;

Performs de-duplication of String without using String.intern. Calling String.intern has negative side effects on the image size because all interned strings must be maintained in an array in the image heap, see StringInternSupport.
/** * Performs de-duplication of String without using {@link String#intern}. Calling * {@link String#intern} has negative side effects on the image size because all interned strings * must be maintained in an array in the image heap, see {@link StringInternSupport}. */
@Platforms(Platform.HOSTED_ONLY.class) public class HostedStringDeduplication { public static HostedStringDeduplication singleton() { return ImageSingletons.lookup(HostedStringDeduplication.class); }
This could really be a Set instead of a Map, but we rely on ConcurrentMap.putIfAbsent. The key and value are always the same.
/** * This could really be a {@link Set} instead of a {@link Map}, but we rely on * {@link ConcurrentMap#putIfAbsent}. The key and value are always the same. */
private final ConcurrentMap<String, String> deduplicatedStrings = new ConcurrentHashMap<>(); HostedStringDeduplication() { }
De-duplicates the provided string. The handling of interned strings depends on the "unintern" parameter: if it is false, then no de-duplication is performed and the interned string is returned; if it is true, then de-duplication is performed on a non-interned copy of the string, i.e., the result is always a non-interned string.
/** * De-duplicates the provided string. * * The handling of interned strings depends on the "unintern" parameter: if it is false, then no * de-duplication is performed and the interned string is returned; if it is true, then * de-duplication is performed on a non-interned copy of the string, i.e., the result is always * a non-interned string. */
public String deduplicate(String s, boolean unintern) { if (s == null) { return null; } String lookup; if (isInternedString(s)) { if (unintern) { /* Make a non-interned copy of the original interned string. */ lookup = new String(s); } else { /* * Do not process interned strings because the result might not be interned, i.e., * the de-duplication could "de-intern" the argument. */ return s; } } else { lookup = s; } String previous = deduplicatedStrings.putIfAbsent(lookup, lookup); String result = previous != null ? previous : lookup; assert !isInternedString(result); return result; }
Returns true if the provided String is interned.
/** * Returns true if the provided {@link String} is interned. */
@SuppressFBWarnings(value = "ES", justification = "Reference equality check needed to detect intern status") public static boolean isInternedString(String str) { /* * Check if we have a string that is interned in the host VM. * * We cannot just check that "str.intern() == str": if the string was not interned before, * then intern() returns the original object and the comparison will succeed. Instead we * first make a copy of the string and intern that. If the result of interning the copy * returns the original String, then the original String was interned before this. * * Calling intern during image generation has a side effect on the hosting HotSpot VM: the * string is put into the string intern table. But it does not have a side effect on the * generated image: We do not put all strings that are interned by HotSpot into the image * heap. We cannot even do that, because that would require access to HotSpot's internal * string table. As long as the interned string is not reachable otherwise, there is no * problem. */ return new String(str).intern() == str; } } @AutomaticFeature class HostedStringDeduplicationFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.add(HostedStringDeduplication.class, new HostedStringDeduplication()); } }