diff --git a/src/Makefile b/src/Makefile
index e0d4c9f..3a945cf 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -25,7 +25,7 @@ PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
 LUA_A=	liblua.a
 CORE_O=	lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
 	lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o  \
-	lundump.o lvm.o lzio.o
+	lundump.o lvm.o lzio.o fnv_hash_32.o
 LIB_O=	lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \
 	lstrlib.o loadlib.o linit.o
 
diff --git a/src/fnv_hash.h b/src/fnv_hash.h
new file mode 100644
index 0000000..37234d3
--- /dev/null
+++ b/src/fnv_hash.h
@@ -0,0 +1,88 @@
+/*
+ * fnv - Fowler/Noll/Vo- hash code
+ *
+ * @(#) $Revision: 1.5 $
+ * @(#) $Id: fnv.h,v 1.5 2003/10/03 20:35:52 chongo Exp $
+ * @(#) $Source: /usr/local/src/cmd/fnv/RCS/fnv.h,v $
+ *
+ ***
+ *
+ * Fowler/Noll/Vo- hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ *      Phong Vo (http://www.research.att.com/info/kpv/)
+ *      Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ *      Landon Curt Noll (http://www.isthe.com/chongo/)
+ *
+ * improved on their algorithm.  Some people tried this hash
+ * and found that it worked rather well.  In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are designed to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate.  See:
+ *
+ *      http://www.isthe.com/chongo/tech/comp/fnv/index.html
+ *
+ * for more details as well as other forms of the FNV hash.
+ *
+ ***
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf().
+ *
+ ***
+ *
+ * Please do not copyright this code.  This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ *	chongo <Landon Curt Noll> /\oo/\
+ *      http://www.isthe.com/chongo/
+ *
+ * Share and Enjoy!	:-)
+ */
+
+#if !defined(__FNV_H__)
+#define __FNV_H__
+
+#define NO_FNV_GCC_OPTIMIZATION 1
+/*
+ * 32 bit FNV-1 hash type
+ */
+typedef unsigned long Fnv32_t;
+
+/*
+ * 32 bit FNV-1 and FNV-1a non-zero initial basis
+ *
+ * The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:
+ *
+ *              chongo <Landon Curt Noll> /\../\
+ *
+ * NOTE: The \'s above are not back-slashing escape characters.
+ * They are literal ASCII  backslash 0x5c characters.
+ *
+ * NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition.
+ */
+#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)
+
+/*
+ * 32 bit magic FNV-1 prime
+ */
+#define FNV_32_PRIME ((Fnv32_t)0x01000193)
+
+extern Fnv32_t fnv_32_buf(const void *buf, size_t len, Fnv32_t hashval);
+
+#endif /* __FNV_H__ */
diff --git a/src/fnv_hash_32.c b/src/fnv_hash_32.c
new file mode 100644
index 0000000..1cd85a4
--- /dev/null
+++ b/src/fnv_hash_32.c
@@ -0,0 +1,101 @@
+/*
+ * hash_32 - 32 bit Fowler/Noll/Vo hash code
+ *
+ * @(#) $Revision: 1.8 $
+ * @(#) $Id: hash_32.c,v 1.8 2003/10/03 20:38:13 chongo Exp $
+ * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_32.c,v $
+ *
+ ***
+ *
+ * Fowler/Noll/Vo hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ *	  Phong Vo (http://www.research.att.com/info/kpv/)
+ *	  Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ *	  Landon Curt Noll (http://www.isthe.com/chongo/)
+ *
+ * improved on their algorithm.  Some people tried this hash
+ * and found that it worked rather well.  In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are designed to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate.  See:
+ *
+ *	  http://www.isthe.com/chongo/tech/comp/fnv/index.html
+ *
+ * for more details as well as other forms of the FNV hash.
+ ***
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf().
+ *
+ ***
+ *
+ * Please do not copyright this code.  This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ *	chongo <Landon Curt Noll> /\oo/\
+ *	  http://www.isthe.com/chongo/
+ *
+ * Share and Enjoy!	:-)
+ */
+
+#include <stdlib.h>
+#include "fnv_hash.h"
+
+
+
+/*
+ * fnv_32_buf - perform a 32 bit Fowler/Noll/Vo hash on a buffer
+ *
+ * input:
+ *	buf	- start of buffer to hash
+ *	len	- length of buffer in octets
+ *	hval	- previous hash value or 0 if first call
+ *
+ * returns:
+ *	32 bit hash as a static hash type
+ *
+ * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval
+ *	 argument on the first call to either fnv_32_buf().
+ */
+Fnv32_t
+fnv_32_buf(const void *buf, size_t len, Fnv32_t hval)
+{
+	unsigned char *bp = (unsigned char *)buf;	/* start of buffer */
+	unsigned char *be = bp + len;		/* beyond end of buffer */
+
+	/*
+	 * FNV-1 hash each octet in the buffer
+	 */
+	while (bp < be) {
+
+	/* multiply by the 32 bit FNV magic prime mod 2^32 */
+#if defined(NO_FNV_GCC_OPTIMIZATION)
+	hval *= FNV_32_PRIME;
+#else
+	hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
+#endif
+
+	/* xor the bottom with the current octet */
+	hval ^= (Fnv32_t)*bp++;
+	}
+
+	/* return our new hash value */
+	return hval;
+}
+
diff --git a/src/lgc.c b/src/lgc.c
index e909c79..ec8d603 100644
--- a/src/lgc.c
+++ b/src/lgc.c
@@ -387,8 +387,7 @@ static void freeobj (lua_State *L, GCObject *o) {
       break;
     }
     case LUA_TSTRING: {
-      G(L)->strt.nuse--;
-      luaM_freemem(L, o, sizestring(gco2ts(o)));
+      luaS_freestr(L, rawgco2ts(o));
       break;
     }
     case LUA_TUSERDATA: {
diff --git a/src/lobject.h b/src/lobject.h
index f1e447e..5553922 100644
--- a/src/lobject.h
+++ b/src/lobject.h
@@ -201,6 +201,7 @@ typedef union TString {
   struct {
     CommonHeader;
     lu_byte reserved;
+    lu_byte hflags;
     unsigned int hash;
     size_t len;
   } tsv;
diff --git a/src/lstate.c b/src/lstate.c
index 4313b83..289adbd 100644
--- a/src/lstate.c
+++ b/src/lstate.c
@@ -6,6 +6,7 @@
 
 
 #include <stddef.h>
+#include <string.h>
 
 #define lstate_c
 #define LUA_CORE
@@ -24,6 +25,40 @@
 #include "ltm.h"
 
 
+/*
+** a macro to help the creation of a unique random seed when a state is
+** created; the seed is used to randomize hashes.
+*/
+#if !defined(luai_makeseed)
+#include <time.h>
+#define luai_makeseed(L)	cast(size_t, time(NULL))
+#endif
+
+
+#include "fnv_hash.h"
+
+/*
+** Compute an initial seed as random as possible. In ANSI, rely on
+** Address Space Layour Randomization (if present) to increase
+** randomness..
+*/
+#define addbuff(b,p,e) \
+  { size_t t = cast(size_t, e); \
+    memcpy(buff + p, &t, sizeof(t)); p += sizeof(t); }
+
+static unsigned int makeseed (lua_State *L) {
+  char buff[4 * sizeof(size_t)];
+  unsigned int h = luai_makeseed();
+  int p = 0;
+  addbuff(buff, p, L);  /* heap variable */
+  addbuff(buff, p, &h);  /* local variable */
+  addbuff(buff, p, luaO_nilobject);  /* global variable */
+  addbuff(buff, p, &lua_newstate);  /* public function */
+  lua_assert(p == sizeof(buff));
+  return fnv_32_buf(buff, p, h ^ FNV1_32_INIT);
+}
+
+
 #define state_size(x)	(sizeof(x) + LUAI_EXTRASPACE)
 #define fromstate(l)	(cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
 #define tostate(l)   (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))
@@ -157,6 +192,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   g->frealloc = f;
   g->ud = ud;
   g->mainthread = L;
+  g->seed = makeseed(L);
   g->uvhead.u.l.prev = &g->uvhead;
   g->uvhead.u.l.next = &g->uvhead;
   g->GCthreshold = 0;  /* mark it as unfinished state */
diff --git a/src/lstate.h b/src/lstate.h
index 3bc575b..198765b 100644
--- a/src/lstate.h
+++ b/src/lstate.h
@@ -71,6 +71,7 @@ typedef struct global_State {
   void *ud;         /* auxiliary data to `frealloc' */
   lu_byte currentwhite;
   lu_byte gcstate;  /* state of garbage collector */
+  unsigned int seed;  /* randomized seed for hashes */
   int sweepstrgc;  /* position of sweep in `strt' */
   GCObject *rootgc;  /* list of all collectable objects */
   GCObject **sweepgc;  /* position of sweep in `rootgc' */
diff --git a/src/lstring.c b/src/lstring.c
index 4911315..09ccd8f 100644
--- a/src/lstring.c
+++ b/src/lstring.c
@@ -17,7 +17,55 @@
 #include "lstate.h"
 #include "lstring.h"
 
+#include <stdio.h>
+#include <stdlib.h>
 
+#define DEPTH_LIMIT 100
+
+#define SHOW_STATS 0
+
+#include "fnv_hash.h"
+
+#define HFLAG_HAS_MARKER     1
+#define HFLAG_IS_MARKER      2
+
+static unsigned int hashstr (const char *str, size_t l, unsigned int seed) {
+  unsigned int h = seed ^ l; /* seed hash */
+  size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */
+  size_t l1;
+  for (l1=l; l1>=step; l1-=step)  /* compute hash */
+    h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
+  return h;
+}
+
+#if SHOW_STATS
+static void str_table_stats (lua_State *L) {
+  stringtable *tb;
+  int empty_buckets = 0;
+  int max_depth = 0;
+  int depth = 0;
+  int i;
+
+  tb = &G(L)->strt;
+  for (i=0; i<tb->size; i++) {
+    GCObject *o = tb->hash[i];
+    if (o == NULL) {
+      empty_buckets++;
+    } else {
+      depth = 0;
+      do {
+        depth++;
+        o = o->gch.next;
+      } while(o != NULL);
+      if (depth > max_depth) {
+        max_depth = depth;
+      }
+    }
+  }
+  printf("lua.stringtable: empty_buckets=%d, nuse=%d, size=%d, max_depth=%d\n",
+    empty_buckets, tb->nuse, tb->size, max_depth);
+}
+#endif
 
 void luaS_resize (lua_State *L, int newsize) {
   GCObject **newhash;
@@ -25,6 +73,10 @@ void luaS_resize (lua_State *L, int newsize) {
   int i;
   if (G(L)->gcstate == GCSsweepstring)
     return;  /* cannot resize during GC traverse */
+#if SHOW_STATS
+  printf("lua.stringtable: newsize=%d\n", newsize);
+  str_table_stats(L);
+#endif
   newhash = luaM_newvector(L, newsize, GCObject *);
   tb = &G(L)->strt;
   for (i=0; i<newsize; i++) newhash[i] = NULL;
@@ -46,15 +98,75 @@ void luaS_resize (lua_State *L, int newsize) {
   tb->hash = newhash;
 }
 
+typedef struct TString_marker {
+  TString ts;
+  int refcount;
+} TString_marker;
+
+
+static TString_marker *new_str_marker (lua_State *L, unsigned int h) {
+  TString_marker *marker;
+  TString *ts;
+  stringtable *tb;
+  unsigned int bucket;
+
+  marker = luaM_new(L, TString_marker);
+  marker->refcount = 0;
+  ts = &(marker->ts);
+  ts->tsv.len = 0;
+  ts->tsv.hflags = HFLAG_IS_MARKER;
+  ts->tsv.hash = h;
+  ts->tsv.marked = luaC_white(G(L));
+  ts->tsv.tt = LUA_TSTRING;
+  ts->tsv.reserved = 0;
+  luaS_fix(ts);
+  tb = &G(L)->strt;
+  bucket = lmod(h, tb->size);
+  ts->tsv.next = tb->hash[bucket];  /* chain new entry */
+  tb->hash[bucket] = obj2gco(ts);
+  tb->nuse++;
+  return marker;
+}
+
+static void release_str_marker (lua_State *L, TString *ts) {
+  stringtable *tb;
+  unsigned int h;
+  unsigned int bucket;
+  GCObject **p;
+  GCObject *o;
+
+  tb = &G(L)->strt;
+
+  /* use orig. hash to find marker. */
+  h = hashstr(getstr(ts), ts->tsv.len, G(L)->seed);
+  bucket = lmod(h, tb->size);  /* old marker position */
+  lua_assert(cast_int(h%tb->size) == lmod(h, tb->size));
+  p = &(tb->hash[bucket]);
+  for (o = *p;o != NULL; p = &(o->gch.next), o = *p) {
+    ts = rawgco2ts(o);
+    if (ts->tsv.hflags & HFLAG_IS_MARKER && ts->tsv.hash == h) {
+      TString_marker *marker = (TString_marker *)ts;
+      if ((--(marker->refcount)) == 0) {
+        *p = o->gch.next;
+        /* free marker. */
+        tb->nuse--;
+        luaM_free(L, marker);
+        break;
+      }
+    }
+  }
+}
 
 static TString *newlstr (lua_State *L, const char *str, size_t l,
-                                       unsigned int h) {
+                                       unsigned int h, lu_byte hflags) {
   TString *ts;
   stringtable *tb;
+  unsigned int bucket;
   if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
     luaM_toobig(L);
   ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));
   ts->tsv.len = l;
+  ts->tsv.hflags = hflags;
   ts->tsv.hash = h;
   ts->tsv.marked = luaC_white(G(L));
   ts->tsv.tt = LUA_TSTRING;
@@ -62,34 +174,95 @@ static TString *newlstr (lua_State *L, const char *str, size_t l,
   memcpy(ts+1, str, l*sizeof(char));
   ((char *)(ts+1))[l] = '\0';  /* ending 0 */
   tb = &G(L)->strt;
-  h = lmod(h, tb->size);
-  ts->tsv.next = tb->hash[h];  /* chain new entry */
-  tb->hash[h] = obj2gco(ts);
+  bucket = lmod(h, tb->size);
+  ts->tsv.next = tb->hash[bucket];  /* chain new entry */
+  tb->hash[bucket] = obj2gco(ts);
   tb->nuse++;
   if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
     luaS_resize(L, tb->size*2);  /* too crowded */
   return ts;
 }
 
+void luaS_freestr (lua_State *L, TString *ts) {
+  stringtable *tb;
+  tb = &G(L)->strt;
+  /* check if string had a marker. */
+  if (ts->tsv.hflags & HFLAG_HAS_MARKER) {
+    release_str_marker(L, ts);
+  } else if (ts->tsv.hflags & HFLAG_IS_MARKER) {
+    /* don't directly free marker objects, they will be freed when there (refcount == 0). */
+    return;
+  }
+  tb->nuse--;
+  luaM_freemem(L, ts, sizestring(&(ts->tsv)));
+}
+
 
 TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
   GCObject *o;
-  unsigned int h = cast(unsigned int, l);  /* seed */
-  size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */
-  size_t l1;
-  for (l1=l; l1>=step; l1-=step)  /* compute hash */
-    h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
-  for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
-       o != NULL;
-       o = o->gch.next) {
-    TString *ts = rawgco2ts(o);
-    if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
-      /* string may be dead */
-      if (isdead(G(L), o)) changewhite(o);
-      return ts;
+  stringtable *tb;
+  unsigned int h1 = 0;
+  unsigned int h;
+  lu_byte hflags = 0;
+  TString_marker *marker = NULL;
+  int first_hash = 1;
+  int depth = 0;
+  TString *ts;
+
+  tb = &G(L)->strt;
+  /* use standard hash function to find initial bucket. */
+  h1 = hashstr(str, l, G(L)->seed);
+  h = h1;
+rehashed:
+  o = tb->hash[lmod(h, tb->size)];
+
+  for (;o != NULL; o = o->gch.next, depth++) {
+    ts = rawgco2ts(o);
+    /* check if hash matches. */
+    if (ts->tsv.hash == h) {
+      /* check if this is a marker value. */
+      if (ts->tsv.hflags & HFLAG_IS_MARKER) {
+        /* we only need the first marker. */
+        if (marker == NULL) {
+          /* found marker with same hash. */
+          marker = (TString_marker *)ts;
+        }
+        continue;
+      }
+      if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
+        /* string may be dead */
+        if (isdead(G(L), o)) changewhite(o);
+        return ts;
+      }
+    }
+  }
+  /* check if we are still on the first hash function. */
+  if (first_hash) {
+    /* if we found a marker. */
+    if (marker) {
+      /* then use the second hash function to find the string. */
+      hflags = HFLAG_HAS_MARKER;
+      first_hash = 0;
+      h = fnv_32_buf(str, l, h ^ FNV1_32_INIT);
+      depth = 0;
+      goto rehashed;
+    }
+    /* if bucket is full */
+    if (depth > DEPTH_LIMIT) {
+      /* rehash string with second hash function */
+      hflags = HFLAG_HAS_MARKER;
+      first_hash = 0;
+      h = fnv_32_buf(str, l, h ^ FNV1_32_INIT);
+      /* create marker. */
+      marker = new_str_marker(L, h1); /* use hash from first hash function. */
+      marker->refcount++;
     }
+  } else {
+    /* second hash function was used. */
+    lua_assert(marker != NULL);
+    marker->refcount++;
   }
-  return newlstr(L, str, l, h);  /* not found */
+  return newlstr(L, str, l, h, hflags);  /* not found */
 }
 
 
diff --git a/src/lstring.h b/src/lstring.h
index 73a2ff8..1dcc4cf 100644
--- a/src/lstring.h
+++ b/src/lstring.h
@@ -26,6 +26,7 @@
 LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
 LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);
 LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+LUAI_FUNC void luaS_freestr (lua_State *L, TString *ts);
 
 
 #endif
