Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/back/commonRef.c
38765 views
/*1* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#if defined(_ALLBSD_SOURCE)26#include <stdint.h> /* for uintptr_t */27#endif2829#include "util.h"30#include "commonRef.h"3132#define ALL_REFS -13334/*35* Each object sent to the front end is tracked with the RefNode struct36* (see util.h).37* External to this module, objects are identified by a jlong id which is38* simply the sequence number. A weak reference is usually used so that39* the presence of a debugger-tracked object will not prevent40* its collection. Once an object is collected, its RefNode may be41* deleted and the weak ref inside may be reused (these may happen in42* either order). Using the sequence number43* as the object id prevents ambiguity in the object id when the weak ref44* is reused. The RefNode* is stored with the object as it's JVMTI Tag.45*46* The ref member is changed from weak to strong when47* gc of the object is to be prevented.48* Whether or not it is strong, it is never exported from this module.49*50* A reference count of each jobject is also maintained here. It tracks51* the number times an object has been referenced through52* commonRef_refToID. A RefNode is freed once the reference53* count is decremented to 0 (with commonRef_release*), even if the54* corresponding object has not been collected.55*56* One hash table is maintained. The mapping of ID to jobject (or RefNode*)57* is handled with one hash table that will re-size itself as the number58* of RefNode's grow.59*/6061/* Initial hash table size (must be power of 2) */62#define HASH_INIT_SIZE 51263/* If element count exceeds HASH_EXPAND_SCALE*hash_size we expand & re-hash */64#define HASH_EXPAND_SCALE 865/* Maximum hash table size (must be power of 2) */66#define HASH_MAX_SIZE (1024*HASH_INIT_SIZE)6768/* Map a key (ID) to a hash bucket */69static jint70hashBucket(jlong key)71{72/* Size should always be a power of 2, use mask instead of mod operator */73/*LINTED*/74return ((jint)key) & (gdata->objectsByIDsize-1);75}7677/* Generate a new ID */78static jlong79newSeqNum(void)80{81return gdata->nextSeqNum++;82}8384/* Create a fresh RefNode structure, create a weak ref and tag the object */85static RefNode *86createNode(JNIEnv *env, jobject ref)87{88RefNode *node;89jobject weakRef;90jvmtiError error;9192/* Could allocate RefNode's in blocks, not sure it would help much */93node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));94if (node == NULL) {95return NULL;96}9798/* Create weak reference to make sure we have a reference */99weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);100if (weakRef == NULL) {101jvmtiDeallocate(node);102return NULL;103}104105/* Set tag on weakRef */106error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)107(gdata->jvmti, weakRef, ptr_to_jlong(node));108if ( error != JVMTI_ERROR_NONE ) {109JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);110jvmtiDeallocate(node);111return NULL;112}113114/* Fill in RefNode */115node->ref = weakRef;116node->isStrong = JNI_FALSE;117node->count = 1;118node->seqNum = newSeqNum();119120/* Count RefNode's created */121gdata->objectsByIDcount++;122return node;123}124125/* Delete a RefNode allocation, delete weak/global ref and clear tag */126static void127deleteNode(JNIEnv *env, RefNode *node)128{129LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));130131if ( node->ref != NULL ) {132/* Clear tag */133(void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)134(gdata->jvmti, node->ref, NULL_OBJECT_ID);135if (node->isStrong) {136JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);137} else {138JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);139}140}141gdata->objectsByIDcount--;142jvmtiDeallocate(node);143}144145/* Change a RefNode to have a strong reference */146static jobject147strengthenNode(JNIEnv *env, RefNode *node)148{149if (!node->isStrong) {150jobject strongRef;151152strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);153/*154* NewGlobalRef on a weak ref will return NULL if the weak155* reference has been collected or if out of memory.156* We need to distinguish those two occurrences.157*/158if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {159EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");160}161if (strongRef != NULL) {162JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);163node->ref = strongRef;164node->isStrong = JNI_TRUE;165}166return strongRef;167} else {168return node->ref;169}170}171172/* Change a RefNode to have a weak reference */173static jweak174weakenNode(JNIEnv *env, RefNode *node)175{176if (node->isStrong) {177jweak weakRef;178179weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);180if (weakRef != NULL) {181JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);182node->ref = weakRef;183node->isStrong = JNI_FALSE;184}185return weakRef;186} else {187return node->ref;188}189}190191/*192* Returns the node which contains the common reference for the193* given object. The passed reference should not be a weak reference194* managed in the object hash table (i.e. returned by commonRef_idToRef)195* because no sequence number checking is done.196*/197static RefNode *198findNodeByRef(JNIEnv *env, jobject ref)199{200jvmtiError error;201jlong tag;202203tag = NULL_OBJECT_ID;204error = JVMTI_FUNC_PTR(gdata->jvmti,GetTag)(gdata->jvmti, ref, &tag);205if ( error == JVMTI_ERROR_NONE ) {206RefNode *node;207208node = (RefNode*)jlong_to_ptr(tag);209return node;210}211return NULL;212}213214/* Locate and delete a node based on ID */215static void216deleteNodeByID(JNIEnv *env, jlong id, jint refCount)217{218jint slot;219RefNode *node;220RefNode *prev;221222slot = hashBucket(id);223node = gdata->objectsByID[slot];224prev = NULL;225226while (node != NULL) {227if (id == node->seqNum) {228if (refCount != ALL_REFS) {229node->count -= refCount;230} else {231node->count = 0;232}233if (node->count <= 0) {234if ( node->count < 0 ) {235EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");236}237/* Detach from id hash table */238if (prev == NULL) {239gdata->objectsByID[slot] = node->next;240} else {241prev->next = node->next;242}243deleteNode(env, node);244}245break;246}247prev = node;248node = node->next;249}250}251252/*253* Returns the node stored in the object hash table for the given object254* id. The id should be a value previously returned by255* commonRef_refToID.256*257* NOTE: It is possible that a match is found here, but that the object258* is garbage collected by the time the caller inspects node->ref.259* Callers should take care using the node->ref object returned here.260*261*/262static RefNode *263findNodeByID(JNIEnv *env, jlong id)264{265jint slot;266RefNode *node;267RefNode *prev;268269slot = hashBucket(id);270node = gdata->objectsByID[slot];271prev = NULL;272273while (node != NULL) {274if ( id == node->seqNum ) {275if ( prev != NULL ) {276/* Re-order hash list so this one is up front */277prev->next = node->next;278node->next = gdata->objectsByID[slot];279gdata->objectsByID[slot] = node;280}281break;282}283node = node->next;284}285return node;286}287288/* Initialize the hash table stored in gdata area */289static void290initializeObjectsByID(int size)291{292/* Size should always be a power of 2 */293if ( size > HASH_MAX_SIZE ) size = HASH_MAX_SIZE;294gdata->objectsByIDsize = size;295gdata->objectsByIDcount = 0;296gdata->objectsByID = (RefNode**)jvmtiAllocate((int)sizeof(RefNode*)*size);297(void)memset(gdata->objectsByID, 0, (int)sizeof(RefNode*)*size);298}299300/* hash in a RefNode */301static void302hashIn(RefNode *node)303{304jint slot;305306/* Add to id hashtable */307slot = hashBucket(node->seqNum);308node->next = gdata->objectsByID[slot];309gdata->objectsByID[slot] = node;310}311312/* Allocate and add RefNode to hash table */313static RefNode *314newCommonRef(JNIEnv *env, jobject ref)315{316RefNode *node;317318/* Allocate the node and set it up */319node = createNode(env, ref);320if ( node == NULL ) {321return NULL;322}323324/* See if hash table needs expansion */325if ( gdata->objectsByIDcount > gdata->objectsByIDsize*HASH_EXPAND_SCALE &&326gdata->objectsByIDsize < HASH_MAX_SIZE ) {327RefNode **old;328int oldsize;329int newsize;330int i;331332/* Save old information */333old = gdata->objectsByID;334oldsize = gdata->objectsByIDsize;335/* Allocate new hash table */336gdata->objectsByID = NULL;337newsize = oldsize*HASH_EXPAND_SCALE;338if ( newsize > HASH_MAX_SIZE ) newsize = HASH_MAX_SIZE;339initializeObjectsByID(newsize);340/* Walk over old one and hash in all the RefNodes */341for ( i = 0 ; i < oldsize ; i++ ) {342RefNode *onode;343344onode = old[i];345while (onode != NULL) {346RefNode *next;347348next = onode->next;349hashIn(onode);350onode = next;351}352}353jvmtiDeallocate(old);354}355356/* Add to id hashtable */357hashIn(node);358return node;359}360361/* Initialize the commonRefs usage */362void363commonRef_initialize(void)364{365gdata->refLock = debugMonitorCreate("JDWP Reference Table Monitor");366gdata->nextSeqNum = 1; /* 0 used for error indication */367initializeObjectsByID(HASH_INIT_SIZE);368}369370/* Reset the commonRefs usage */371void372commonRef_reset(JNIEnv *env)373{374debugMonitorEnter(gdata->refLock); {375int i;376377for (i = 0; i < gdata->objectsByIDsize; i++) {378RefNode *node;379380node = gdata->objectsByID[i];381while (node != NULL) {382RefNode *next;383384next = node->next;385deleteNode(env, node);386node = next;387}388gdata->objectsByID[i] = NULL;389}390391/* Toss entire hash table and re-create a new one */392jvmtiDeallocate(gdata->objectsByID);393gdata->objectsByID = NULL;394gdata->nextSeqNum = 1; /* 0 used for error indication */395initializeObjectsByID(HASH_INIT_SIZE);396397} debugMonitorExit(gdata->refLock);398}399400/*401* Given a reference obtained from JNI or JVMTI, return an object402* id suitable for sending to the debugger front end.403*/404jlong405commonRef_refToID(JNIEnv *env, jobject ref)406{407jlong id;408409if (ref == NULL) {410return NULL_OBJECT_ID;411}412413id = NULL_OBJECT_ID;414debugMonitorEnter(gdata->refLock); {415RefNode *node;416417node = findNodeByRef(env, ref);418if (node == NULL) {419node = newCommonRef(env, ref);420if ( node != NULL ) {421id = node->seqNum;422}423} else {424id = node->seqNum;425node->count++;426}427} debugMonitorExit(gdata->refLock);428return id;429}430431/*432* Given an object ID obtained from the debugger front end, return a433* strong, global reference to that object (or NULL if the object434* has been collected). The reference can then be used for JNI and435* JVMTI calls. Caller is resposible for deleting the returned reference.436*/437jobject438commonRef_idToRef(JNIEnv *env, jlong id)439{440jobject ref;441442ref = NULL;443debugMonitorEnter(gdata->refLock); {444RefNode *node;445446node = findNodeByID(env, id);447if (node != NULL) {448if (node->isStrong) {449saveGlobalRef(env, node->ref, &ref);450} else {451jobject lref;452453lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);454if ( lref == NULL ) {455/* Object was GC'd shortly after we found the node */456deleteNodeByID(env, node->seqNum, ALL_REFS);457} else {458saveGlobalRef(env, node->ref, &ref);459JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);460}461}462}463} debugMonitorExit(gdata->refLock);464return ref;465}466467/* Deletes the global reference that commonRef_idToRef() created */468void469commonRef_idToRef_delete(JNIEnv *env, jobject ref)470{471if ( ref==NULL ) {472return;473}474tossGlobalRef(env, &ref);475}476477478/* Prevent garbage collection of an object */479jvmtiError480commonRef_pin(jlong id)481{482jvmtiError error;483484error = JVMTI_ERROR_NONE;485if (id == NULL_OBJECT_ID) {486return error;487}488debugMonitorEnter(gdata->refLock); {489JNIEnv *env;490RefNode *node;491492env = getEnv();493node = findNodeByID(env, id);494if (node == NULL) {495error = AGENT_ERROR_INVALID_OBJECT;496} else {497jobject strongRef;498499strongRef = strengthenNode(env, node);500if (strongRef == NULL) {501/*502* Referent has been collected, clean up now.503*/504error = AGENT_ERROR_INVALID_OBJECT;505deleteNodeByID(env, id, ALL_REFS);506}507}508} debugMonitorExit(gdata->refLock);509return error;510}511512/* Permit garbage collection of an object */513jvmtiError514commonRef_unpin(jlong id)515{516jvmtiError error;517518error = JVMTI_ERROR_NONE;519debugMonitorEnter(gdata->refLock); {520JNIEnv *env;521RefNode *node;522523env = getEnv();524node = findNodeByID(env, id);525if (node != NULL) {526jweak weakRef;527528weakRef = weakenNode(env, node);529if (weakRef == NULL) {530error = AGENT_ERROR_OUT_OF_MEMORY;531}532}533} debugMonitorExit(gdata->refLock);534return error;535}536537/* Release tracking of an object by ID */538void539commonRef_release(JNIEnv *env, jlong id)540{541debugMonitorEnter(gdata->refLock); {542deleteNodeByID(env, id, 1);543} debugMonitorExit(gdata->refLock);544}545546void547commonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount)548{549debugMonitorEnter(gdata->refLock); {550deleteNodeByID(env, id, refCount);551} debugMonitorExit(gdata->refLock);552}553554/* Get rid of RefNodes for objects that no longer exist */555void556commonRef_compact(void)557{558JNIEnv *env;559RefNode *node;560RefNode *prev;561int i;562563env = getEnv();564debugMonitorEnter(gdata->refLock); {565if ( gdata->objectsByIDsize > 0 ) {566/*567* Walk through the id-based hash table. Detach any nodes568* for which the ref has been collected.569*/570for (i = 0; i < gdata->objectsByIDsize; i++) {571node = gdata->objectsByID[i];572prev = NULL;573while (node != NULL) {574/* Has the object been collected? */575if ( (!node->isStrong) &&576isSameObject(env, node->ref, NULL)) {577RefNode *freed;578579/* Detach from the ID list */580if (prev == NULL) {581gdata->objectsByID[i] = node->next;582} else {583prev->next = node->next;584}585freed = node;586node = node->next;587deleteNode(env, freed);588} else {589prev = node;590node = node->next;591}592}593}594}595} debugMonitorExit(gdata->refLock);596}597598/* Lock the commonRef tables */599void600commonRef_lock(void)601{602debugMonitorEnter(gdata->refLock);603}604605/* Unlock the commonRef tables */606void607commonRef_unlock(void)608{609debugMonitorExit(gdata->refLock);610}611612613