It's ref counting, so most objects are freed as soon as the last reference goes away. Cycles are handled by a backup cycle collector using trial deletion (Bacon–Rajan style). Any object whose refcount drops without hitting zero becomes a candidate, and the collector traces from those candidates. In practice cycles are rare, and refcounting does almost all the work.
There were a lot of hurdles along the way and we solve them as we come across them. Keeping values native when JS uses them dynamically was a challenge. d = new Date(); d.foo = 1 is easy if you box everything, but then it's a slow interpreter (like some other projects in the field). So we had to invent a new mechanism; native types get a side table for extra properties.
Generics and union types were also challenging: one JS generic can need several different C++ layouts.
eval is limited: new Function runs on a small evaluator written in C++, and direct eval isn't supported yet.